1 /*!
2  * \file   include/TFEL/Math/ExpressionTemplates/Expr.hxx
3  * \brief
4  * \author Thomas Helfer
5  * \date   04 févr. 2015
6  * \copyright Copyright (C) 2006-2018 CEA/DEN, EDF R&D. All rights
7  * reserved.
8  * This project is publicly released under either the GNU GPL Licence
9  * or the CECILL-A licence. A copy of thoses licences are delivered
10  * with the sources of TFEL. CEA or EDF may also distribute this
11  * project under specific licensing conditions.
12  */
13 
14 #ifndef LIB_TFEL_MATH_EXPRESSION_EXPR_HXX
15 #define LIB_TFEL_MATH_EXPRESSION_EXPR_HXX
16 
17 #include<type_traits>
18 #include"TFEL/Metaprogramming/ResultOf.hxx"
19 #include"TFEL/Math/General/BasicOperations.hxx"
20 #include"TFEL/Math/General/ComputeObjectTag.hxx"
21 #include"TFEL/Math/General/ConceptRebind.hxx"
22 #include"TFEL/Math/General/ComputeUnaryResult.hxx"
23 #include"TFEL/Math/General/ComputeBinaryResult.hxx"
24 
25 namespace tfel{
26 
27   namespace math{
28 
29     /*!
30      * an helper structure used for the partial specialisation of the
31      * Expr class.
32      * \tparam T  : type of unary operation argument
33      * \tparam Op : the unary operation
34      */
35     template<typename T1,typename Op>
36     struct UnaryOperation;
37     /*!
38      * an helper structure used for the partial specialisation of the
39      * Expr class.
40      * \tparam T1 : type of the left  argument of the binary operation
41      * \tparam T2 : type of the right argument of the binary operation
42      * \tparam Op : the binary operation
43      */
44     template<typename T1,typename T2,typename Op>
45     struct BinaryOperation;
46     /*!
47      * an helper structure used for the partial specialisation of the
48      * Expr class.
49      * \tparam T1 : type of the left  argument of the binary operation
50      * \tparam T2 : type of the right argument of the binary operation
51      * \tparam Op : the binary operation
52      * \pre T1 must be a scalar
53      */
54     template<typename T1,typename T2,typename Op>
55     struct ScalarObjectOperation;
56     /*!
57      * an helper structure used for the partial specialisation of the
58      * Expr class.
59      * \tparam T1 : type of the left  argument of the binary operation
60      * \tparam T2 : type of the right argument of the binary operation
61      * \tparam Op : the binary operation
62      * \pre T1 must be a scalar
63      */
64     template<typename T1,typename T2,typename Op>
65     struct ObjectScalarOperation;
66     /*!
67      * an helper structure used for the partial specialisation of the
68      * Expr class.
69      * \tparam T1 : type of the left  argument of the diadic product
70      * \tparam T2 : type of the right argument of the diadic product
71      */
72     template<typename T1,typename T2>
73     struct DiadicProductOperation;
74 
75     /*!
76      * an helper class
77      */
78     struct ExprBase
79     {
80     protected:
81       /*!
82        * a simple metafunction defining how an argument of an
83        * operation is stored in the Expr class.
84        */
85       template<typename T>
86       using ArgumentStorage = typename std::conditional<std::is_rvalue_reference<T>::value,
87 							const typename std::decay<T>::type,
88 							const typename std::decay<T>::type&>::type;
89     }; // end of strut ExprBase
90 
91     /*!
92      * \brief An helper class which defines array-like access operator
93      * by relying on the operator() of the derived class.
94      * \tparam Child : child class
95      */
96     template<typename Child>
97     struct ExprWithArrayAccessOperator
98     {
99       // /*!
100       //  * \brief array like access operator
101       //  * \param[in] i : index
102       //  */
103       // auto operator[](const typename Child::size_type i) const
104       // 	-> decltype(std::declval<const Child>()(i))
105       // {
106       // 	return static_cast<const Child&>(this).operator()(i);
107       // }
108       // /*!
109       //  * \brief array like access operator
110       //  * \param[in] i : index
111       //  */
112       // auto operator[](const size_type i)
113       // 	-> decltype(std::declval<Child>()(i))
114       // {
115       // 	return static_cast<const Child&>(this).operator()(i);
116       // }
117     }; // end of struct ExprWithArrayAccessOperator
118 
119     /*!
120      * \brief an Expr object allows the lazy evaluation of a mathematical
121      * operation.
122      * \tparam ResultType : the type of the result of the operation
123      * \tparam Operation  : Operation to be performed
124      *
125      * Partial specialisation are provided for:
126      * - unary operations (negation, function call)
127      * - binary operations
128      */
129     template<typename ResultType,typename Operation>
130     struct Expr
131       :  public ConceptRebind<typename ComputeObjectTag<ResultType>::type,
132 			      Expr<ResultType,Operation>>::type,
133 	 public Operation
134     {
135       //! a simple alias
136       typedef typename Operation::value_type        value_type;
137       //! a simple alias
138       typedef typename Operation::size_type 	    size_type;
139       /*!
140        * default constructor
141        * \param[in] args : arguments of Operation constructor
142        */
143       template<typename... Args>
Exprtfel::math::Expr144       explicit TFEL_MATH_INLINE Expr(Args&&... args)
145 	: Operation(std::forward<Args>(args)...)
146       {}
147       /*!
148        * \brief array like access operator
149        * \param[in] i : index
150        * \note This operator is always defined, even tough it might not be
151        * valid
152        */
153       value_type
operator []tfel::math::Expr154       operator[](const size_type i) const
155       {
156 	// may not be defined
157 	static_assert(tfel::meta::IsConstCallable<Expr,size_type>::cond,
158 		      "Expr is not callable");
159 	return Operation::operator()(i);
160       }
161       /*!
162        * \brief array like access operator
163        * \param[in] i : index
164        * \note This operator is always defined, even tough it might not be
165        * valid
166        */
167       value_type&
operator []tfel::math::Expr168       operator[](const size_type i)
169       {
170 	// may not be defined
171 	static_assert(std::is_same<decltype(Operation::operator()(i)),
172 				   value_type&>::value,
173 		      "unexpected return value for Operation::operator(size_type)");
174 	return Operation::operator()(i);
175       }
176 
177       using Operation::operator();
178       using Operation::operator=;
179     };
180 
181     /*!
182      * an Expr object allows the lazy evaluation of an operation.
183      * \tparam ResultType :
184      * \tparam T1 : type of the left  argument of the binary operation
185      * \tparam T2 : type of the right argument of the binary operation
186      * \tparam Op : the binary operation
187      */
188     template<typename ResultType,typename T1,typename Op>
189     struct Expr<ResultType,UnaryOperation<T1,Op> >
190       : public ExprBase,
191 	public ConceptRebind<typename ComputeObjectTag<ResultType>::type,
192 			     Expr<ResultType,UnaryOperation<T1,Op>>>::type
193     {
194       Expr() = delete;
195       Expr(const Expr&) = default;
196       Expr(Expr&&) = default;
197       Expr& operator=(const Expr&) = delete;
198       Expr& operator=(Expr&&) = delete;
199       //! type return by the access operator(s)
200       using value_type = typename ResultType::value_type;
201       //! type used by access operator(s)
202       using size_type  = typename ResultType::size_type;
203       //! result type
204       using result_type = ResultType;
205       //! argument type
206       using argument_type = T1;
207       //! storage type of the argument
208       using argument_storage_type = ArgumentStorage<T1>;
209       /*!
210        * \param l : left argument of the binary operation
211        * \param r : right argument of the binary operation
212        */
Exprtfel::math::Expr213       TFEL_MATH_INLINE Expr(T1 l)
214 	: a(l)
215       {} // end of Expr
216       /*!
217        * multidimensional access operator
218        */
219       template<typename... Indexes>
220       TFEL_MATH_INLINE auto
operator ()tfel::math::Expr221       operator()(const Indexes... i) const
222 	-> UnaryOperationHandler<typename std::conditional<std::is_lvalue_reference<typename tfel::meta::ResultOf<argument_storage_type,Indexes...>::type>::value,
223 							   typename tfel::meta::ResultOf<argument_storage_type,Indexes...>::type,
224 							   typename tfel::meta::ResultOf<argument_storage_type,Indexes...>::type&&>::type,Op>
225       {
226 	static_assert(isUnaryOperationResultTypeValid<typename tfel::meta::ResultOf<argument_storage_type,Indexes...>::type,Op>::value,
227 		      "invalid call to unary operation operator()");
228 	return Op::apply(this->a(i...));
229       } // end of operator()
230     protected:
231       argument_storage_type a; //<! argument of the operation
232     }; // end of struct Expr<ResultType,UnaryOperation<T1,Op> >
233 
234     /*!
235      * an Expr object allows the lazy evaluation of a binary
236      * operation.
237      * \tparam ResultType :
238      * \tparam T1 : type of the left  argument of the binary operation
239      * \tparam T2 : type of the right argument of the binary operation
240      * \tparam Op : the binary operation
241      */
242     template<typename ResultType,typename T1,typename T2,typename Op>
243     struct Expr<ResultType,BinaryOperation<T1,T2,Op> >
244       : public ExprBase,
245 	public ConceptRebind<typename ComputeObjectTag<ResultType>::type,
246 			     Expr<ResultType,BinaryOperation<T1,T2,Op> > >::type
247     {
248       Expr() = delete;
249       Expr(const Expr&) = default;
250       Expr(Expr&&) = default;
251       Expr& operator=(const Expr&) = delete;
252       Expr& operator=(Expr&&) = delete;
253       //! type return by the access operator(s)
254       using value_type = typename ResultType::value_type;
255       //! type used by access operator(s)
256       using size_type  = typename ResultType::size_type;
257       //! result type
258       using result_type = ResultType;
259       //! type of left hand side argument
260       using lhs_type = T1;
261       //! type of right hand side argument
262       using rhs_type = T2;
263       //! storage type of left hand side argument
264       using lhs_storage_type = ArgumentStorage<T1>;
265       //! storage type of right hand side argument
266       using rhs_storage_type = ArgumentStorage<T2>;
267       /*!
268        * \param l : left argument of the binary operation
269        * \param r : right argument of the binary operation
270        */
Exprtfel::math::Expr271       TFEL_MATH_INLINE Expr(T1 l,T2 r)
272 	: a(l),b(r)
273       {} // end of Expr
274       /*!
275        * array-like access operator
276        */
277       TFEL_MATH_INLINE value_type
operator []tfel::math::Expr278       operator[](const size_type i) const
279       {
280       	static_assert(isBinaryOperationResultTypeValid<typename tfel::meta::ResultOf<lhs_storage_type,size_type>::type,
281       						       typename tfel::meta::ResultOf<rhs_storage_type,size_type>::type,Op>::value,
282       		      "invalid call to unary operation operator[]");
283       	return Op::apply(this->a(i),this->b(i));
284       }
285       /*!
286        * multi-dimensional operator acces
287        */
288       template<typename... Indexes>
289       TFEL_MATH_INLINE
290       BinaryOperationHandler<typename std::conditional<std::is_lvalue_reference<typename tfel::meta::ResultOf<lhs_storage_type,Indexes...>::type>::value,
291 						       typename tfel::meta::ResultOf<lhs_storage_type,Indexes...>::type,
292 						       typename tfel::meta::ResultOf<lhs_storage_type,Indexes...>::type&&>::type,
293 			     typename std::conditional<std::is_lvalue_reference<typename tfel::meta::ResultOf<rhs_storage_type,Indexes...>::type>::value,
294 						       typename tfel::meta::ResultOf<rhs_storage_type,Indexes...>::type,
295 						       typename tfel::meta::ResultOf<rhs_storage_type,Indexes...>::type&&>::type,Op>
operator ()tfel::math::Expr296       operator()(const Indexes... i) const
297       {
298 	static_assert(isBinaryOperationResultTypeValid<typename tfel::meta::ResultOf<lhs_storage_type,Indexes...>::type,
299                                        		       typename tfel::meta::ResultOf<rhs_storage_type,Indexes...>::type,Op>::value,
300 		      "invalid call to binary operation operator()");
301 	return Op::apply(this->a(i...),this->b(i...));
302       } // end of operator()
303     protected:
304       lhs_storage_type a; //<! left  hand side argument of the operation
305       rhs_storage_type b; //<! right hand side argument of the operation
306     }; // end of struct Expr<ResultType,BinaryOperation<T1,T2,Op> >
307 
308     /*!
309      * an Expr object allows the lazy evaluation of a binary
310      * operation between a scalar and a mathematical object.
311      * \tparam ResultType :
312      * \tparam T1 : type of the left  argument of the binary operation
313      * \tparam T2 : type of the right argument of the binary operation
314      * \tparam Op : the binary operation
315      */
316     template<typename ResultType,typename T1,
317 	     typename T2,typename Op>
318     struct Expr<ResultType,ScalarObjectOperation<T1,T2,Op>>
319       : public ExprBase,
320 	public ConceptRebind<typename ComputeObjectTag<ResultType>::type,
321 			     Expr<ResultType,ScalarObjectOperation<T1,T2,Op> > >::type
322     {
323       Expr() = delete;
324       Expr(const Expr&) = default;
325       Expr(Expr&&) = default;
326       Expr& operator=(const Expr&) = delete;
327       Expr& operator=(Expr&&) = delete;
328       //! type return by the access operator(s)
329       using value_type = typename ResultType::value_type;
330       //! type used by access operator(s)
331       using size_type  = typename ResultType::size_type;
332       //! result type
333       using result_type = ResultType;
334       //! type of left hand side argument
335       using lhs_type = T1;
336       //! type of right hand side argument
337       using rhs_type = T2;
338       //! storage type of left hand side argument
339       using lhs_storage_type = ArgumentStorage<T1>;
340       //! storage type of right hand side argument
341       using rhs_storage_type = ArgumentStorage<T2>;
342       /*!
343        * \param l : left argument of the binary operation
344        * \param r : right argument of the binary operation
345        */
Exprtfel::math::Expr346       TFEL_MATH_INLINE Expr(T1 l,T2 r)
347 	: a(l),b(r)
348       {} // end of Expr
349       /*!
350        * \brief array-like access operator
351        */
352       TFEL_MATH_INLINE value_type
operator []tfel::math::Expr353       operator[](const size_type i) const
354       {
355       	static_assert(isBinaryOperationResultTypeValid<typename std::add_lvalue_reference<lhs_storage_type>::type,
356       						       typename tfel::meta::ResultOf<rhs_storage_type,size_type>::type,Op>::value,
357       		      "invalid call");
358       	return Op::apply(this->a,this->b(i));
359       }
360       /*!
361        * \brief multi-dimensional operator acces
362        */
363       template<typename... Indexes>
364       TFEL_MATH_INLINE
365       BinaryOperationHandler<typename std::add_lvalue_reference<lhs_storage_type>::type,
366 			     typename std::conditional<std::is_lvalue_reference<typename tfel::meta::ResultOf<rhs_storage_type,Indexes...>::type>::value,
367 						       typename tfel::meta::ResultOf<rhs_storage_type,Indexes...>::type,
368 						       typename tfel::meta::ResultOf<rhs_storage_type,Indexes...>::type&&>::type,Op>
operator ()tfel::math::Expr369       operator()(const Indexes... i) const
370       {
371 	static_assert(isBinaryOperationResultTypeValid<typename std::add_lvalue_reference<lhs_storage_type>::type,
372 		      typename tfel::meta::ResultOf<rhs_storage_type,Indexes...>::type,Op>::value,
373 		      "invalid call");
374 	return Op::apply(this->a,this->b(i...));
375       } // end of operator()
376     protected:
377       const lhs_storage_type a; //<! left  hand side argument of the operation
378       const rhs_storage_type b; //<! right hand side argument of the operation
379     }; // end of struct Expr<ResultType,ScalarObjectOperation<T1,T2,Op>>
380 
381     /*!
382      * an Expr object allows the lazy evaluation of a binary
383      * operation between a mathematical object and scalar.
384      * \tparam ResultType :
385      * \tparam T1 : type of the left  argument of the binary operation
386      * \tparam T2 : type of the right argument of the binary operation
387      * \tparam Op : the binary operation
388      */
389     template<typename ResultType,typename T1,
390 	     typename T2,typename Op>
391     struct Expr<ResultType,ObjectScalarOperation<T1,T2,Op> >
392       : public ExprBase,
393 	public ConceptRebind<typename ComputeObjectTag<ResultType>::type,
394 			     Expr<ResultType,ObjectScalarOperation<T1,T2,Op> > >::type
395     {
396       Expr() = delete;
397       Expr(const Expr&) = default;
398       Expr(Expr&&) = default;
399       Expr& operator=(const Expr&) = delete;
400       Expr& operator=(Expr&&) = delete;
401       //! type return by the access operator(s)
402       using value_type = typename ResultType::value_type;
403       //! type used by access operator(s)
404       using size_type  = typename ResultType::size_type;
405       //! result type
406       using result_type = ResultType;
407       //! type of left hand side argument
408       using lhs_type = T1;
409       //! type of right hand side argument
410       using rhs_type = T2;
411       //! storage type of left hand side argument
412       using lhs_storage_type = ArgumentStorage<T1>;
413       //! storage type of right hand side argument
414       using rhs_storage_type = ArgumentStorage<T2>;
415       /*!
416        * \param l : left argument of the binary operation
417        * \param r : right argument of the binary operation
418        */
Exprtfel::math::Expr419       TFEL_MATH_INLINE Expr(T1 l,T2 r)
420 	: a(l),b(r)
421       {} // end of Expr
422       /*!
423        * array-like access operator
424        */
425       TFEL_MATH_INLINE value_type
operator []tfel::math::Expr426       operator[](const size_type i) const
427       {
428       	static_assert(isBinaryOperationResultTypeValid<typename tfel::meta::ResultOf<lhs_storage_type,size_type>::type,
429       						       typename std::add_lvalue_reference<rhs_storage_type>::type,Op>::value,
430       		      "invalid call");
431       	return Op::apply(this->a(i),this->b);
432       }
433       /*!
434        * multi-dimensional operator acces
435        */
436       template<typename... Indexes>
437       TFEL_MATH_INLINE
438       BinaryOperationHandler<typename std::conditional<std::is_lvalue_reference<typename tfel::meta::ResultOf<lhs_storage_type,Indexes...>::type>::value,
439 						       typename tfel::meta::ResultOf<lhs_storage_type,Indexes...>::type,
440 						       typename tfel::meta::ResultOf<lhs_storage_type,Indexes...>::type&&>::type,
441 			     typename std::add_lvalue_reference<rhs_storage_type>::type,Op>
operator ()tfel::math::Expr442       operator()(const Indexes... i) const
443       {
444 	static_assert(isBinaryOperationResultTypeValid<typename tfel::meta::ResultOf<lhs_storage_type,Indexes...>::type,
445 						       typename std::add_lvalue_reference<rhs_storage_type>::type,Op>::value,
446 		      "invalid call");
447 	return Op::apply(this->a(i...),this->b);
448       } // end of operator()
449     protected:
450       lhs_storage_type a; //<! left  hand side argument of the operation
451       rhs_storage_type b; //<! right hand side argument of the operation
452     }; // end of struct Expr<ResultType,ObjectScalarOperation<T1,T2,Op>>
453 
454     /*!
455      * an Expr object allows the lazy evaluation of a diadic product
456      * \tparam ResultType :
457      * \tparam T1 : type of the left  argument of the diadic product
458      * \tparam T2 : type of the right argument of the diadic product
459      */
460     template<typename ResultType,typename T1,typename T2>
461     struct Expr<ResultType,DiadicProductOperation<T1,T2>>
462       : public ExprBase,
463 	public ConceptRebind<typename ComputeObjectTag<ResultType>::type,
464 			     Expr<ResultType,DiadicProductOperation<T1,T2>>>::type
465     {
466       Expr() = delete;
467       Expr(const Expr&) = default;
468       Expr(Expr&&) = default;
469       Expr& operator=(const Expr&) = delete;
470       Expr& operator=(Expr&&) = delete;
471       //! type return by the access operator(s)
472       using value_type = typename ResultType::value_type;
473       //! type used by access operator(s)
474       using size_type  = typename ResultType::size_type;
475       //! result type
476       using result_type = ResultType;
477       //! type of left hand side argument
478       using lhs_type = T1;
479       //! type of right hand side argument
480       using rhs_type = T2;
481       //! storage type of left hand side argument
482       using lhs_storage_type = ArgumentStorage<T1>;
483       //! storage type of right hand side argument
484       using rhs_storage_type = ArgumentStorage<T2>;
485       /*!
486        * \param l : left argument of the binary operation
487        * \param r : right argument of the binary operation
488        */
Exprtfel::math::Expr489       TFEL_MATH_INLINE Expr(T1 l,T2 r)
490 	: a(l),b(r)
491       {} // end of Expr
492       /*!
493        * access operator
494        * \param[in] i : row index
495        * \param[in] i : column index
496        */
497       TFEL_MATH_INLINE
498       BinaryOperationHandler<typename tfel::meta::ResultOf<lhs_storage_type,size_type>::type,
499 			     typename tfel::meta::ResultOf<rhs_storage_type,size_type>::type,OpMult>
operator ()tfel::math::Expr500       operator()(const size_type i,
501 		 const size_type j) const
502 
503       {
504       	return (this->a(i))*(this->b(j));
505       }
506     protected:
507       lhs_storage_type a; //<! left  hand side argument of the operation
508       rhs_storage_type b; //<! right hand side argument of the operation
509     }; // end of struct Expr<ResultType,DiadicProductOperation<T1,T2,Op>>
510 
511     /*!
512      * \brief evaluate an expression
513      * \return the computed value
514      * \param[in] e: expression
515      */
516     template<typename ResultType,typename Operation>
517     ResultType eval(const Expr<ResultType,Operation>& e){
518       return {e};
519     } // end of eval
520 
521   } // end of namespace math
522 
523 } // end of namespace tfel
524 
525 #endif /* LIB_TFEL_MATH_EXPRESSION_EXPR_HXX */
526