2010-10-25 10:15:22 -04:00
// This file is part of Eigen, a lightweight C++ template library
2009-05-22 20:25:33 +02:00
// for linear algebra.
2008-08-24 15:15:32 +00:00
//
2010-06-24 23:21:58 +02:00
// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
2008-11-24 13:40:43 +00:00
// Copyright (C) 2006-2008 Benoit Jacob <jacob.benoit.1@gmail.com>
2008-08-24 15:15:32 +00:00
//
2012-07-13 14:42:47 -04:00
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
2008-08-24 15:15:32 +00:00
# ifndef EIGEN_XPRHELPER_H
# define EIGEN_XPRHELPER_H
// just a workaround because GCC seems to not really like empty structs
2010-07-04 15:26:58 +02:00
// FIXME: gcc 4.3 generates bad code when strict-aliasing is enabled
// so currently we simply disable this optimization for gcc 4.3
2014-11-04 21:58:52 +01:00
# if EIGEN_COMP_GNUC && !EIGEN_GNUC_AT(4,3)
2009-12-18 17:08:59 +01:00
# define EIGEN_EMPTY_STRUCT_CTOR(X) \
2013-02-07 19:06:14 +01:00
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE X ( ) { } \
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE X ( const X & ) { }
2008-08-24 15:15:32 +00:00
# else
2009-12-18 17:08:59 +01:00
# define EIGEN_EMPTY_STRUCT_CTOR(X)
2008-08-24 15:15:32 +00:00
# endif
2012-04-15 11:06:28 +01:00
namespace Eigen {
2010-10-25 10:15:22 -04:00
namespace internal {
2014-12-04 22:48:53 +01:00
template < typename IndexDest , typename IndexSrc >
EIGEN_DEVICE_FUNC
inline IndexDest convert_index ( const IndexSrc & idx ) {
// for sizeof(IndexDest)>=sizeof(IndexSrc) compilers should be able to optimize this away:
eigen_internal_assert ( idx < = NumTraits < IndexDest > : : highest ( ) & & " Index value to big for target type " ) ;
return IndexDest ( idx ) ;
}
2016-06-23 14:27:20 +02:00
// promote_scalar_arg is an helper used in operation between an expression and a scalar, like:
// expression * scalar
// Its role is to determine how the type T of the scalar operand should be promoted given the scalar type ExprScalar of the given expression.
2016-07-20 09:56:39 +02:00
// The IsSupported template parameter must be provided by the caller as: internal::has_ReturnType<ScalarBinaryOpTraits<ExprScalar,T,op> >::value using the proper order for ExprScalar and T.
2016-06-23 14:27:20 +02:00
// Then the logic is as follows:
// - if the operation is natively supported as defined by IsSupported, then the scalar type is not promoted, and T is returned.
2016-06-24 11:28:54 +02:00
// - otherwise, NumTraits<ExprScalar>::Literal is returned if T is implicitly convertible to NumTraits<ExprScalar>::Literal AND that this does not imply a float to integer conversion.
// - otherwise, ExprScalar is returned if T is implicitly convertible to ExprScalar AND that this does not imply a float to integer conversion.
2016-06-23 14:27:20 +02:00
// - In all other cases, the promoted type is not defined, and the respective operation is thus invalid and not available (SFINAE).
2016-06-24 11:28:54 +02:00
template < typename ExprScalar , typename T , bool IsSupported >
struct promote_scalar_arg ;
2016-06-23 14:27:20 +02:00
2016-06-24 11:28:54 +02:00
template < typename S , typename T >
struct promote_scalar_arg < S , T , true >
2016-06-23 14:27:20 +02:00
{
typedef T type ;
} ;
2016-06-24 11:28:54 +02:00
// Recursively check safe conversion to PromotedType, and then ExprScalar if they are different.
template < typename ExprScalar , typename T , typename PromotedType ,
bool ConvertibleToLiteral = internal : : is_convertible < T , PromotedType > : : value ,
bool IsSafe = NumTraits < T > : : IsInteger | | ! NumTraits < PromotedType > : : IsInteger >
struct promote_scalar_arg_unsupported ;
// Start recursion with NumTraits<ExprScalar>::Literal
2016-06-23 14:27:20 +02:00
template < typename S , typename T >
2016-06-24 11:28:54 +02:00
struct promote_scalar_arg < S , T , false > : promote_scalar_arg_unsupported < S , T , typename NumTraits < S > : : Literal > { } ;
// We found a match!
template < typename S , typename T , typename PromotedType >
struct promote_scalar_arg_unsupported < S , T , PromotedType , true , true >
2016-06-23 14:27:20 +02:00
{
2016-06-24 11:28:54 +02:00
typedef PromotedType type ;
2016-06-23 14:27:20 +02:00
} ;
2016-06-24 11:28:54 +02:00
// No match, but no real-to-integer issues, and ExprScalar and current PromotedType are different,
// so let's try to promote to ExprScalar
template < typename ExprScalar , typename T , typename PromotedType >
struct promote_scalar_arg_unsupported < ExprScalar , T , PromotedType , false , true >
: promote_scalar_arg_unsupported < ExprScalar , T , ExprScalar >
{ } ;
// Unsafe real-to-integer, let's stop.
2016-06-24 23:10:39 +02:00
template < typename S , typename T , typename PromotedType , bool ConvertibleToLiteral >
struct promote_scalar_arg_unsupported < S , T , PromotedType , ConvertibleToLiteral , false > { } ;
2016-06-24 11:28:54 +02:00
// T is not even convertible to ExprScalar, let's stop.
template < typename S , typename T >
struct promote_scalar_arg_unsupported < S , T , S , false , true > { } ;
2010-10-25 10:15:22 -04:00
//classes inheriting no_assignment_operator don't generate a default operator=.
class no_assignment_operator
2008-08-24 15:15:32 +00:00
{
private :
2010-10-25 10:15:22 -04:00
no_assignment_operator & operator = ( const no_assignment_operator & ) ;
2008-08-24 15:15:32 +00:00
} ;
2010-06-03 08:41:11 +02:00
/** \internal return the index type with the largest number of bits */
template < typename I1 , typename I2 >
2010-10-25 10:15:22 -04:00
struct promote_index_type
2010-06-03 08:41:11 +02:00
{
2010-10-25 22:13:49 +02:00
typedef typename conditional < ( sizeof ( I1 ) < sizeof ( I2 ) ) , I2 , I1 > : : type type ;
2010-06-03 08:41:11 +02:00
} ;
2010-05-30 16:00:58 -04:00
/** \internal If the template parameter Value is Dynamic, this class is just a wrapper around a T variable that
2008-11-30 21:49:02 +00:00
* can be accessed using value ( ) and setValue ( ) .
* Otherwise , this class is an empty structure and value ( ) just returns the template parameter Value .
*/
2010-10-25 10:15:22 -04:00
template < typename T , int Value > class variable_if_dynamic
2008-08-24 15:15:32 +00:00
{
public :
2010-10-25 10:15:22 -04:00
EIGEN_EMPTY_STRUCT_CTOR ( variable_if_dynamic )
2016-05-31 17:23:42 +02:00
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit variable_if_dynamic ( T v ) { EIGEN_ONLY_USED_FOR_DEBUG ( v ) ; eigen_assert ( v = = T ( Value ) ) ; }
EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE T value ( ) { return T ( Value ) ; }
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void setValue ( T ) { }
2008-08-24 15:15:32 +00:00
} ;
2010-10-25 10:15:22 -04:00
template < typename T > class variable_if_dynamic < T , Dynamic >
2008-08-24 15:15:32 +00:00
{
2010-05-30 16:00:58 -04:00
T m_value ;
2013-02-07 19:06:14 +01:00
EIGEN_DEVICE_FUNC variable_if_dynamic ( ) { eigen_assert ( false ) ; }
2008-08-24 15:15:32 +00:00
public :
2016-05-31 17:23:42 +02:00
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit variable_if_dynamic ( T value ) : m_value ( value ) { }
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE T value ( ) const { return m_value ; }
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void setValue ( T value ) { m_value = value ; }
2012-07-10 23:04:17 +02:00
} ;
/** \internal like variable_if_dynamic but for DynamicIndex
*/
template < typename T , int Value > class variable_if_dynamicindex
{
public :
EIGEN_EMPTY_STRUCT_CTOR ( variable_if_dynamicindex )
2016-05-31 17:23:42 +02:00
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit variable_if_dynamicindex ( T v ) { EIGEN_ONLY_USED_FOR_DEBUG ( v ) ; eigen_assert ( v = = T ( Value ) ) ; }
EIGEN_DEVICE_FUNC static EIGEN_STRONG_INLINE T value ( ) { return T ( Value ) ; }
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void setValue ( T ) { }
2012-07-10 23:04:17 +02:00
} ;
template < typename T > class variable_if_dynamicindex < T , DynamicIndex >
{
T m_value ;
2013-02-07 19:06:14 +01:00
EIGEN_DEVICE_FUNC variable_if_dynamicindex ( ) { eigen_assert ( false ) ; }
2012-07-10 23:04:17 +02:00
public :
2016-05-31 17:23:42 +02:00
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit variable_if_dynamicindex ( T value ) : m_value ( value ) { }
EIGEN_DEVICE_FUNC T EIGEN_STRONG_INLINE value ( ) const { return m_value ; }
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void setValue ( T value ) { m_value = value ; }
2008-08-24 15:15:32 +00:00
} ;
2010-10-25 10:15:22 -04:00
template < typename T > struct functor_traits
2008-08-24 15:15:32 +00:00
{
enum
{
Cost = 10 ,
2013-06-12 09:22:59 +02:00
PacketAccess = false ,
IsRepeatable = false
2008-08-24 15:15:32 +00:00
} ;
} ;
2010-10-25 10:15:22 -04:00
template < typename T > struct packet_traits ;
2008-08-24 15:15:32 +00:00
2010-10-25 10:15:22 -04:00
template < typename T > struct unpacket_traits
2008-08-24 15:15:32 +00:00
{
typedef T type ;
2014-04-17 23:26:34 +02:00
typedef T half ;
2015-08-07 10:44:01 +02:00
enum
{
size = 1 ,
alignment = 1
} ;
2008-08-24 15:15:32 +00:00
} ;
2015-08-07 20:05:31 +02:00
template < int Size , typename PacketType ,
2015-09-02 22:51:43 +02:00
bool Stop = Size = = Dynamic | | ( Size % unpacket_traits < PacketType > : : size ) = = 0 | | is_same < PacketType , typename unpacket_traits < PacketType > : : half > : : value >
2015-08-07 20:05:31 +02:00
struct find_best_packet_helper ;
template < int Size , typename PacketType >
struct find_best_packet_helper < Size , PacketType , true >
{
typedef PacketType type ;
} ;
template < int Size , typename PacketType >
struct find_best_packet_helper < Size , PacketType , false >
{
typedef typename find_best_packet_helper < Size , typename unpacket_traits < PacketType > : : half > : : type type ;
} ;
template < typename T , int Size >
struct find_best_packet
{
typedef typename find_best_packet_helper < Size , typename packet_traits < T > : : type > : : type type ;
} ;
2015-08-06 14:07:59 +02:00
# if EIGEN_MAX_STATIC_ALIGN_BYTES>0
template < int ArrayBytes , int AlignmentBytes ,
bool Match = bool ( ( ArrayBytes % AlignmentBytes ) = = 0 ) ,
2016-10-20 19:19:37 +02:00
bool TryHalf = bool ( EIGEN_MIN_ALIGN_BYTES < AlignmentBytes ) >
2015-08-06 14:07:59 +02:00
struct compute_default_alignment_helper
{
enum { value = 0 } ;
} ;
template < int ArrayBytes , int AlignmentBytes , bool TryHalf >
struct compute_default_alignment_helper < ArrayBytes , AlignmentBytes , true , TryHalf > // Match
{
enum { value = AlignmentBytes } ;
} ;
template < int ArrayBytes , int AlignmentBytes >
struct compute_default_alignment_helper < ArrayBytes , AlignmentBytes , false , true > // Try-half
{
// current packet too large, try with an half-packet
enum { value = compute_default_alignment_helper < ArrayBytes , AlignmentBytes / 2 > : : value } ;
} ;
# else
// If static alignment is disabled, no need to bother.
// This also avoids a division by zero in "bool Match = bool((ArrayBytes%AlignmentBytes)==0)"
template < int ArrayBytes , int AlignmentBytes >
struct compute_default_alignment_helper
{
enum { value = 0 } ;
} ;
# endif
template < typename T , int Size > struct compute_default_alignment {
enum { value = compute_default_alignment_helper < Size * sizeof ( T ) , EIGEN_MAX_STATIC_ALIGN_BYTES > : : value } ;
} ;
template < typename T > struct compute_default_alignment < T , Dynamic > {
enum { value = EIGEN_MAX_ALIGN_BYTES } ;
} ;
2010-06-24 15:13:41 +02:00
template < typename _Scalar , int _Rows , int _Cols ,
int _Options = AutoAlign |
( ( _Rows = = 1 & & _Cols ! = 1 ) ? RowMajor
: ( _Cols = = 1 & & _Rows ! = 1 ) ? ColMajor
: EIGEN_DEFAULT_MATRIX_STORAGE_ORDER_OPTION ) ,
int _MaxRows = _Rows ,
int _MaxCols = _Cols
2010-10-25 10:15:22 -04:00
> class make_proper_matrix_type
2010-06-24 15:13:41 +02:00
{
enum {
IsColVector = _Cols = = 1 & & _Rows ! = 1 ,
IsRowVector = _Rows = = 1 & & _Cols ! = 1 ,
Options = IsColVector ? ( _Options | ColMajor ) & ~ RowMajor
: IsRowVector ? ( _Options | RowMajor ) & ~ ColMajor
: _Options
} ;
public :
typedef Matrix < _Scalar , _Rows , _Cols , Options , _MaxRows , _MaxCols > type ;
} ;
2014-03-12 13:34:11 +01:00
template < typename Scalar , int Rows , int Cols , int Options , int MaxRows , int MaxCols >
class compute_matrix_flags
{
enum { row_major_bit = Options & RowMajor ? RowMajorBit : 0 } ;
public :
// FIXME currently we still have to handle DirectAccessBit at the expression level to handle DenseCoeffsBase<>
// and then propagate this information to the evaluator's flags.
// However, I (Gael) think that DirectAccessBit should only matter at the evaluation stage.
enum { ret = DirectAccessBit | LvalueBit | NestByRefBit | row_major_bit } ;
} ;
2010-10-25 10:15:22 -04:00
template < int _Rows , int _Cols > struct size_at_compile_time
2008-08-24 15:15:32 +00:00
{
enum { ret = ( _Rows = = Dynamic | | _Cols = = Dynamic ) ? Dynamic : _Rows * _Cols } ;
} ;
2014-06-25 17:22:12 +02:00
template < typename XprType > struct size_of_xpr_at_compile_time
{
enum { ret = size_at_compile_time < traits < XprType > : : RowsAtCompileTime , traits < XprType > : : ColsAtCompileTime > : : ret } ;
} ;
2010-10-25 10:15:22 -04:00
/* plain_matrix_type : the difference from eval is that plain_matrix_type is always a plain matrix type,
* whereas eval is a const reference in the case of a matrix
2008-12-18 20:36:25 +00:00
*/
2010-10-25 10:15:22 -04:00
template < typename T , typename StorageKind = typename traits < T > : : StorageKind > struct plain_matrix_type ;
2015-10-14 10:12:58 +02:00
template < typename T , typename BaseClassType , int Flags > struct plain_matrix_type_dense ;
2010-10-25 10:15:22 -04:00
template < typename T > struct plain_matrix_type < T , Dense >
2009-12-17 19:28:54 +01:00
{
2015-10-14 10:12:58 +02:00
typedef typename plain_matrix_type_dense < T , typename traits < T > : : XprKind , traits < T > : : Flags > : : type type ;
2009-12-17 19:28:54 +01:00
} ;
2014-07-01 17:51:53 +02:00
template < typename T > struct plain_matrix_type < T , DiagonalShape >
{
typedef typename T : : PlainObject type ;
} ;
2009-12-17 19:28:54 +01:00
2015-10-14 10:12:58 +02:00
template < typename T , int Flags > struct plain_matrix_type_dense < T , MatrixXpr , Flags >
2008-08-24 15:15:32 +00:00
{
2010-10-25 10:15:22 -04:00
typedef Matrix < typename traits < T > : : Scalar ,
traits < T > : : RowsAtCompileTime ,
traits < T > : : ColsAtCompileTime ,
2015-10-14 10:12:58 +02:00
AutoAlign | ( Flags & RowMajorBit ? RowMajor : ColMajor ) ,
2010-10-25 10:15:22 -04:00
traits < T > : : MaxRowsAtCompileTime ,
traits < T > : : MaxColsAtCompileTime
2008-08-24 15:15:32 +00:00
> type ;
} ;
2015-10-14 10:12:58 +02:00
template < typename T , int Flags > struct plain_matrix_type_dense < T , ArrayXpr , Flags >
2009-12-17 19:28:54 +01:00
{
2010-10-25 10:15:22 -04:00
typedef Array < typename traits < T > : : Scalar ,
traits < T > : : RowsAtCompileTime ,
traits < T > : : ColsAtCompileTime ,
2015-10-14 10:12:58 +02:00
AutoAlign | ( Flags & RowMajorBit ? RowMajor : ColMajor ) ,
2010-10-25 10:15:22 -04:00
traits < T > : : MaxRowsAtCompileTime ,
traits < T > : : MaxColsAtCompileTime
2009-12-17 19:28:54 +01:00
> type ;
} ;
2010-10-25 10:15:22 -04:00
/* eval : the return type of eval(). For matrices, this is just a const reference
2009-12-17 19:28:54 +01:00
* in order to avoid a useless copy
*/
2010-10-25 10:15:22 -04:00
template < typename T , typename StorageKind = typename traits < T > : : StorageKind > struct eval ;
2009-12-17 19:28:54 +01:00
2010-10-25 10:15:22 -04:00
template < typename T > struct eval < T , Dense >
2009-12-17 19:28:54 +01:00
{
2010-10-25 10:15:22 -04:00
typedef typename plain_matrix_type < T > : : type type ;
2010-02-20 15:53:57 +01:00
// typedef typename T::PlainObject type;
2010-10-25 10:15:22 -04:00
// typedef T::Matrix<typename traits<T>::Scalar,
// traits<T>::RowsAtCompileTime,
// traits<T>::ColsAtCompileTime,
// AutoAlign | (traits<T>::Flags&RowMajorBit ? RowMajor : ColMajor),
// traits<T>::MaxRowsAtCompileTime,
// traits<T>::MaxColsAtCompileTime
2009-12-17 19:28:54 +01:00
// > type;
} ;
2014-07-01 17:51:53 +02:00
template < typename T > struct eval < T , DiagonalShape >
{
typedef typename plain_matrix_type < T > : : type type ;
} ;
2008-12-18 20:36:25 +00:00
// for matrices, no need to evaluate, just use a const reference to avoid a useless copy
2010-03-19 02:12:23 -04:00
template < typename _Scalar , int _Rows , int _Cols , int _Options , int _MaxRows , int _MaxCols >
2010-10-25 10:15:22 -04:00
struct eval < Matrix < _Scalar , _Rows , _Cols , _Options , _MaxRows , _MaxCols > , Dense >
2008-12-18 20:36:25 +00:00
{
2010-03-19 02:12:23 -04:00
typedef const Matrix < _Scalar , _Rows , _Cols , _Options , _MaxRows , _MaxCols > & type ;
2008-12-18 20:36:25 +00:00
} ;
2010-03-19 02:12:23 -04:00
template < typename _Scalar , int _Rows , int _Cols , int _Options , int _MaxRows , int _MaxCols >
2010-10-25 10:15:22 -04:00
struct eval < Array < _Scalar , _Rows , _Cols , _Options , _MaxRows , _MaxCols > , Dense >
2008-12-18 20:36:25 +00:00
{
2010-03-19 02:12:23 -04:00
typedef const Array < _Scalar , _Rows , _Cols , _Options , _MaxRows , _MaxCols > & type ;
2008-12-18 20:36:25 +00:00
} ;
2008-10-13 13:14:43 +00:00
2009-12-17 19:28:54 +01:00
2015-10-14 10:12:58 +02:00
/* similar to plain_matrix_type, but using the evaluator's Flags */
template < typename T , typename StorageKind = typename traits < T > : : StorageKind > struct plain_object_eval ;
template < typename T >
struct plain_object_eval < T , Dense >
{
typedef typename plain_matrix_type_dense < T , typename traits < T > : : XprKind , evaluator < T > : : Flags > : : type type ;
} ;
2009-12-17 19:28:54 +01:00
2010-10-25 10:15:22 -04:00
/* plain_matrix_type_column_major : same as plain_matrix_type but guaranteed to be column-major
2008-12-18 20:36:25 +00:00
*/
2010-10-25 10:15:22 -04:00
template < typename T > struct plain_matrix_type_column_major
2008-10-13 13:14:43 +00:00
{
2010-10-25 10:15:22 -04:00
enum { Rows = traits < T > : : RowsAtCompileTime ,
Cols = traits < T > : : ColsAtCompileTime ,
MaxRows = traits < T > : : MaxRowsAtCompileTime ,
MaxCols = traits < T > : : MaxColsAtCompileTime
2010-03-21 11:28:03 -04:00
} ;
2010-10-25 10:15:22 -04:00
typedef Matrix < typename traits < T > : : Scalar ,
2010-03-21 11:28:03 -04:00
Rows ,
Cols ,
2010-04-16 10:13:32 -04:00
( MaxRows = = 1 & & MaxCols ! = 1 ) ? RowMajor : ColMajor ,
MaxRows ,
MaxCols
2008-10-13 13:14:43 +00:00
> type ;
} ;
2010-10-25 10:15:22 -04:00
/* plain_matrix_type_row_major : same as plain_matrix_type but guaranteed to be row-major
2009-05-27 19:24:05 +02:00
*/
2010-10-25 10:15:22 -04:00
template < typename T > struct plain_matrix_type_row_major
2009-05-27 19:24:05 +02:00
{
2010-10-25 10:15:22 -04:00
enum { Rows = traits < T > : : RowsAtCompileTime ,
Cols = traits < T > : : ColsAtCompileTime ,
MaxRows = traits < T > : : MaxRowsAtCompileTime ,
MaxCols = traits < T > : : MaxColsAtCompileTime
2010-03-21 11:28:03 -04:00
} ;
2010-10-25 10:15:22 -04:00
typedef Matrix < typename traits < T > : : Scalar ,
2010-03-21 11:28:03 -04:00
Rows ,
Cols ,
2010-04-16 10:13:32 -04:00
( MaxCols = = 1 & & MaxRows ! = 1 ) ? RowMajor : ColMajor ,
MaxRows ,
MaxCols
2009-05-27 19:24:05 +02:00
> type ;
} ;
2012-02-03 23:18:26 +01:00
/** \internal The reference selector for template expressions. The idea is that we don't
* need to use references for expressions since they are light weight proxy
* objects which should generate no copying overhead . */
2009-11-30 18:56:56 +01:00
template < typename T >
2010-10-25 10:15:22 -04:00
struct ref_selector
2009-11-30 18:56:56 +01:00
{
2010-10-25 22:13:49 +02:00
typedef typename conditional <
2010-10-25 10:15:22 -04:00
bool ( traits < T > : : Flags & NestByRefBit ) ,
2010-02-06 17:43:32 +01:00
T const & ,
2012-02-03 23:18:26 +01:00
const T
> : : type type ;
2015-06-19 18:12:40 +02:00
typedef typename conditional <
bool ( traits < T > : : Flags & NestByRefBit ) ,
T & ,
T
> : : type non_const_type ;
2012-02-03 23:18:26 +01:00
} ;
/** \internal Adds the const qualifier on the value-type of T2 if and only if T1 is a const type */
template < typename T1 , typename T2 >
struct transfer_constness
{
typedef typename conditional <
bool ( internal : : is_const < T1 > : : value ) ,
typename internal : : add_const_on_value_type < T2 > : : type ,
T2
2010-10-25 22:13:49 +02:00
> : : type type ;
2009-11-30 18:56:56 +01:00
} ;
2013-11-25 15:20:31 +01:00
2014-03-10 09:28:00 +01:00
// However, we still need a mechanism to detect whether an expression which is evaluated multiple time
// has to be evaluated into a temporary.
2014-09-18 15:15:27 +02:00
// That's the purpose of this new nested_eval helper:
/** \internal Determines how a given expression should be nested when evaluated multiple times.
* For example , when you do a * ( b + c ) , Eigen will determine how the expression b + c should be
* evaluated into the bigger product expression . The choice is between nesting the expression b + c as - is , or
* evaluating that expression b + c into a temporary variable d , and nest d so that the resulting expression is
* a * d . Evaluating can be beneficial for example if every coefficient access in the resulting expression causes
* many coefficient accesses in the nested expressions - - as is the case with matrix product for example .
*
2016-01-01 21:45:06 +01:00
* \ tparam T the type of the expression being nested .
* \ tparam n the number of coefficient accesses in the nested expression for each coefficient access in the bigger expression .
* \ tparam PlainObject the type of the temporary if needed .
2014-09-18 15:15:27 +02:00
*/
2015-10-14 10:12:58 +02:00
template < typename T , int n , typename PlainObject = typename plain_object_eval < T > : : type > struct nested_eval
2014-03-10 09:28:00 +01:00
{
enum {
ScalarReadCost = NumTraits < typename traits < T > : : Scalar > : : ReadCost ,
2015-10-09 14:57:51 +02:00
CoeffReadCost = evaluator < T > : : CoeffReadCost , // NOTE What if an evaluator evaluate itself into a tempory?
// Then CoeffReadCost will be small (e.g., 1) but we still have to evaluate, especially if n>1.
// This situation is already taken care by the EvalBeforeNestingBit flag, which is turned ON
// for all evaluator creating a temporary. This flag is then propagated by the parent evaluators.
// Another solution could be to count the number of temps?
2015-10-28 11:42:14 +01:00
NAsInteger = n = = Dynamic ? HugeCost : n ,
CostEval = ( NAsInteger + 1 ) * ScalarReadCost + CoeffReadCost ,
2016-11-30 17:59:13 +01:00
CostNoEval = NAsInteger * CoeffReadCost ,
Evaluate = ( int ( evaluator < T > : : Flags ) & EvalBeforeNestingBit ) | | ( int ( CostEval ) < int ( CostNoEval ) )
2014-03-10 09:28:00 +01:00
} ;
2016-11-30 17:59:13 +01:00
typedef typename conditional < Evaluate , PlainObject , typename ref_selector < T > : : type > : : type type ;
2014-03-10 09:28:00 +01:00
} ;
2011-01-07 05:16:01 -05:00
template < typename T >
2013-02-07 19:06:14 +01:00
EIGEN_DEVICE_FUNC
2014-10-28 14:49:44 +01:00
inline T * const_cast_ptr ( const T * ptr )
2011-01-07 05:16:01 -05:00
{
return const_cast < T * > ( ptr ) ;
}
2010-10-25 10:15:22 -04:00
template < typename Derived , typename XprKind = typename traits < Derived > : : XprKind >
struct dense_xpr_base
2010-04-23 12:16:30 -04:00
{
2010-10-25 10:15:22 -04:00
/* dense_xpr_base should only ever be used on dense expressions, thus falling either into the MatrixXpr or into the ArrayXpr cases */
2010-04-23 12:16:30 -04:00
} ;
template < typename Derived >
2010-10-25 10:15:22 -04:00
struct dense_xpr_base < Derived , MatrixXpr >
2010-04-23 12:16:30 -04:00
{
typedef MatrixBase < Derived > type ;
} ;
template < typename Derived >
2010-10-25 10:15:22 -04:00
struct dense_xpr_base < Derived , ArrayXpr >
2010-04-23 12:16:30 -04:00
{
typedef ArrayBase < Derived > type ;
} ;
2014-06-20 15:39:38 +02:00
template < typename Derived , typename XprKind = typename traits < Derived > : : XprKind , typename StorageKind = typename traits < Derived > : : StorageKind >
struct generic_xpr_base ;
template < typename Derived , typename XprKind >
struct generic_xpr_base < Derived , XprKind , Dense >
{
typedef typename dense_xpr_base < Derived , XprKind > : : type type ;
} ;
2010-10-25 10:15:22 -04:00
template < typename XprType , typename CastType > struct cast_return_type
2008-10-25 22:38:22 +00:00
{
2009-04-29 17:39:39 +00:00
typedef typename XprType : : Scalar CurrentScalarType ;
2010-10-26 16:47:01 +02:00
typedef typename remove_all < CastType > : : type _CastType ;
2009-04-29 17:39:39 +00:00
typedef typename _CastType : : Scalar NewScalarType ;
2010-10-25 22:13:49 +02:00
typedef typename conditional < is_same < CurrentScalarType , NewScalarType > : : value ,
const XprType & , CastType > : : type type ;
2008-10-25 22:38:22 +00:00
} ;
2010-10-25 10:15:22 -04:00
template < typename A , typename B > struct promote_storage_type ;
2009-11-16 18:19:08 +01:00
2010-10-25 10:15:22 -04:00
template < typename A > struct promote_storage_type < A , A >
2009-11-16 18:19:08 +01:00
{
typedef A ret ;
} ;
2014-10-02 10:30:44 -07:00
template < typename A > struct promote_storage_type < A , const A >
{
typedef A ret ;
} ;
template < typename A > struct promote_storage_type < const A , A >
{
typedef A ret ;
} ;
2009-11-16 18:19:08 +01:00
2014-07-01 17:51:53 +02:00
/** \internal Specify the "storage kind" of applying a coefficient-wise
* binary operations between two expressions of kinds A and B respectively .
* The template parameter Functor permits to specialize the resulting storage kind wrt to
* the functor .
* The default rules are as follows :
* \ code
2016-01-30 14:39:50 +01:00
* A op A - > A
* A op dense - > dense
* dense op B - > dense
* sparse op dense - > sparse
* dense op sparse - > sparse
2014-07-01 17:51:53 +02:00
* \ endcode
*/
template < typename A , typename B , typename Functor > struct cwise_promote_storage_type ;
2016-01-30 14:39:50 +01:00
template < typename A , typename Functor > struct cwise_promote_storage_type < A , A , Functor > { typedef A ret ; } ;
template < typename Functor > struct cwise_promote_storage_type < Dense , Dense , Functor > { typedef Dense ret ; } ;
template < typename A , typename Functor > struct cwise_promote_storage_type < A , Dense , Functor > { typedef Dense ret ; } ;
template < typename B , typename Functor > struct cwise_promote_storage_type < Dense , B , Functor > { typedef Dense ret ; } ;
template < typename Functor > struct cwise_promote_storage_type < Sparse , Dense , Functor > { typedef Sparse ret ; } ;
template < typename Functor > struct cwise_promote_storage_type < Dense , Sparse , Functor > { typedef Sparse ret ; } ;
2014-07-01 17:51:53 +02:00
/** \internal Specify the "storage kind" of multiplying an expression of kind A with kind B.
* The template parameter ProductTag permits to specialize the resulting storage kind wrt to
* some compile - time properties of the product : GemmProduct , GemvProduct , OuterProduct , InnerProduct .
* The default rules are as follows :
* \ code
* K * K - > K
* dense * K - > dense
* K * dense - > dense
* diag * K - > K
* K * diag - > K
2014-07-30 15:22:50 +02:00
* Perm * K - > K
* K * Perm - > K
2014-07-01 17:51:53 +02:00
* \ endcode
*/
template < typename A , typename B , int ProductTag > struct product_promote_storage_type ;
2014-07-30 15:22:50 +02:00
template < typename A , int ProductTag > struct product_promote_storage_type < A , A , ProductTag > { typedef A ret ; } ;
template < int ProductTag > struct product_promote_storage_type < Dense , Dense , ProductTag > { typedef Dense ret ; } ;
template < typename A , int ProductTag > struct product_promote_storage_type < A , Dense , ProductTag > { typedef Dense ret ; } ;
template < typename B , int ProductTag > struct product_promote_storage_type < Dense , B , ProductTag > { typedef Dense ret ; } ;
template < typename A , int ProductTag > struct product_promote_storage_type < A , DiagonalShape , ProductTag > { typedef A ret ; } ;
template < typename B , int ProductTag > struct product_promote_storage_type < DiagonalShape , B , ProductTag > { typedef B ret ; } ;
template < int ProductTag > struct product_promote_storage_type < Dense , DiagonalShape , ProductTag > { typedef Dense ret ; } ;
template < int ProductTag > struct product_promote_storage_type < DiagonalShape , Dense , ProductTag > { typedef Dense ret ; } ;
template < typename A , int ProductTag > struct product_promote_storage_type < A , PermutationStorage , ProductTag > { typedef A ret ; } ;
template < typename B , int ProductTag > struct product_promote_storage_type < PermutationStorage , B , ProductTag > { typedef B ret ; } ;
template < int ProductTag > struct product_promote_storage_type < Dense , PermutationStorage , ProductTag > { typedef Dense ret ; } ;
template < int ProductTag > struct product_promote_storage_type < PermutationStorage , Dense , ProductTag > { typedef Dense ret ; } ;
2009-11-16 18:19:08 +01:00
2010-10-21 10:13:17 +02:00
/** \internal gives the plain matrix or array type to store a row/column/diagonal of a matrix type.
2016-01-01 21:45:06 +01:00
* \ tparam Scalar optional parameter allowing to pass a different scalar type than the one of the MatrixType .
2010-03-19 02:12:23 -04:00
*/
2010-10-21 10:13:17 +02:00
template < typename ExpressionType , typename Scalar = typename ExpressionType : : Scalar >
2010-10-25 10:15:22 -04:00
struct plain_row_type
2010-03-19 02:12:23 -04:00
{
2010-10-21 10:13:17 +02:00
typedef Matrix < Scalar , 1 , ExpressionType : : ColsAtCompileTime ,
ExpressionType : : PlainObject : : Options | RowMajor , 1 , ExpressionType : : MaxColsAtCompileTime > MatrixRowType ;
typedef Array < Scalar , 1 , ExpressionType : : ColsAtCompileTime ,
ExpressionType : : PlainObject : : Options | RowMajor , 1 , ExpressionType : : MaxColsAtCompileTime > ArrayRowType ;
2010-10-25 22:13:49 +02:00
typedef typename conditional <
is_same < typename traits < ExpressionType > : : XprKind , MatrixXpr > : : value ,
2010-10-21 10:13:17 +02:00
MatrixRowType ,
ArrayRowType
2010-10-25 22:13:49 +02:00
> : : type type ;
2010-03-19 02:12:23 -04:00
} ;
2010-10-21 10:13:17 +02:00
template < typename ExpressionType , typename Scalar = typename ExpressionType : : Scalar >
2010-10-25 10:15:22 -04:00
struct plain_col_type
2010-03-19 02:12:23 -04:00
{
2010-10-21 10:13:17 +02:00
typedef Matrix < Scalar , ExpressionType : : RowsAtCompileTime , 1 ,
ExpressionType : : PlainObject : : Options & ~ RowMajor , ExpressionType : : MaxRowsAtCompileTime , 1 > MatrixColType ;
typedef Array < Scalar , ExpressionType : : RowsAtCompileTime , 1 ,
ExpressionType : : PlainObject : : Options & ~ RowMajor , ExpressionType : : MaxRowsAtCompileTime , 1 > ArrayColType ;
2010-10-25 22:13:49 +02:00
typedef typename conditional <
is_same < typename traits < ExpressionType > : : XprKind , MatrixXpr > : : value ,
2010-10-21 10:13:17 +02:00
MatrixColType ,
ArrayColType
2010-10-25 22:13:49 +02:00
> : : type type ;
2010-03-19 02:12:23 -04:00
} ;
2010-10-21 10:13:17 +02:00
template < typename ExpressionType , typename Scalar = typename ExpressionType : : Scalar >
2010-10-25 10:15:22 -04:00
struct plain_diag_type
2010-03-19 02:12:23 -04:00
{
2010-10-21 10:13:17 +02:00
enum { diag_size = EIGEN_SIZE_MIN_PREFER_DYNAMIC ( ExpressionType : : RowsAtCompileTime , ExpressionType : : ColsAtCompileTime ) ,
max_diag_size = EIGEN_SIZE_MIN_PREFER_FIXED ( ExpressionType : : MaxRowsAtCompileTime , ExpressionType : : MaxColsAtCompileTime )
2010-03-19 02:12:23 -04:00
} ;
2010-10-21 10:13:17 +02:00
typedef Matrix < Scalar , diag_size , 1 , ExpressionType : : PlainObject : : Options & ~ RowMajor , max_diag_size , 1 > MatrixDiagType ;
typedef Array < Scalar , diag_size , 1 , ExpressionType : : PlainObject : : Options & ~ RowMajor , max_diag_size , 1 > ArrayDiagType ;
2010-10-25 22:13:49 +02:00
typedef typename conditional <
is_same < typename traits < ExpressionType > : : XprKind , MatrixXpr > : : value ,
2010-10-21 10:13:17 +02:00
MatrixDiagType ,
ArrayDiagType
2010-10-25 22:13:49 +02:00
> : : type type ;
2010-03-19 02:12:23 -04:00
} ;
2016-06-14 11:26:57 +02:00
template < typename Expr , typename Scalar = typename Expr : : Scalar >
struct plain_constant_type
{
enum { Options = ( traits < Expr > : : Flags & RowMajorBit ) ? RowMajor : 0 } ;
typedef Array < Scalar , traits < Expr > : : RowsAtCompileTime , traits < Expr > : : ColsAtCompileTime ,
Options , traits < Expr > : : MaxRowsAtCompileTime , traits < Expr > : : MaxColsAtCompileTime > array_type ;
typedef Matrix < Scalar , traits < Expr > : : RowsAtCompileTime , traits < Expr > : : ColsAtCompileTime ,
Options , traits < Expr > : : MaxRowsAtCompileTime , traits < Expr > : : MaxColsAtCompileTime > matrix_type ;
typedef CwiseNullaryOp < scalar_constant_op < Scalar > , const typename conditional < is_same < typename traits < Expr > : : XprKind , MatrixXpr > : : value , matrix_type , array_type > : : type > type ;
} ;
2010-12-22 17:45:37 -05:00
template < typename ExpressionType >
struct is_lvalue
{
enum { value = ! bool ( is_const < ExpressionType > : : value ) & &
bool ( traits < ExpressionType > : : Flags & LvalueBit ) } ;
} ;
2014-02-18 11:03:59 +01:00
template < typename T > struct is_diagonal
{ enum { ret = false } ; } ;
template < typename T > struct is_diagonal < DiagonalBase < T > >
{ enum { ret = true } ; } ;
template < typename T > struct is_diagonal < DiagonalWrapper < T > >
{ enum { ret = true } ; } ;
template < typename T , int S > struct is_diagonal < DiagonalMatrix < T , S > >
{ enum { ret = true } ; } ;
2014-07-22 11:35:56 +02:00
template < typename S1 , typename S2 > struct glue_shapes ;
template < > struct glue_shapes < DenseShape , TriangularShape > { typedef TriangularShape type ; } ;
2015-06-10 10:09:04 +02:00
template < typename T1 , typename T2 >
bool is_same_dense ( const T1 & mat1 , const T2 & mat2 , typename enable_if < has_direct_access < T1 > : : ret & & has_direct_access < T2 > : : ret , T1 > : : type * = 0 )
{
return ( mat1 . data ( ) = = mat2 . data ( ) ) & & ( mat1 . innerStride ( ) = = mat2 . innerStride ( ) ) & & ( mat1 . outerStride ( ) = = mat2 . outerStride ( ) ) ;
}
template < typename T1 , typename T2 >
bool is_same_dense ( const T1 & , const T2 & , typename enable_if < ! ( has_direct_access < T1 > : : ret & & has_direct_access < T2 > : : ret ) , T1 > : : type * = 0 )
{
return false ;
}
2016-09-08 08:36:27 +02:00
// Internal helper defining the cost of a scalar division for the type T.
// The default heuristic can be specialized for each scalar type and architecture.
2016-12-27 16:31:17 +01:00
template < typename T , bool Vectorized = false , typename EnableIf = void >
2016-09-08 08:36:27 +02:00
struct scalar_div_cost {
enum { value = 8 * NumTraits < T > : : MulCost } ;
} ;
2016-09-21 17:26:04 +02:00
template < typename T , bool Vectorized >
struct scalar_div_cost < std : : complex < T > , Vectorized > {
enum { value = 2 * scalar_div_cost < T > : : value
+ 6 * NumTraits < T > : : MulCost
+ 3 * NumTraits < T > : : AddCost
} ;
} ;
2016-09-08 08:36:27 +02:00
template < bool Vectorized >
2016-09-09 22:34:38 +02:00
struct scalar_div_cost < signed long , Vectorized , typename conditional < sizeof ( long ) = = 8 , void , false_type > : : type > { enum { value = 24 } ; } ;
2016-09-08 08:36:27 +02:00
template < bool Vectorized >
2016-09-09 22:34:38 +02:00
struct scalar_div_cost < unsigned long , Vectorized , typename conditional < sizeof ( long ) = = 8 , void , false_type > : : type > { enum { value = 21 } ; } ;
2016-09-08 08:36:27 +02:00
2015-10-27 10:38:49 +01:00
# ifdef EIGEN_DEBUG_ASSIGN
std : : string demangle_traversal ( int t )
{
if ( t = = DefaultTraversal ) return " DefaultTraversal " ;
if ( t = = LinearTraversal ) return " LinearTraversal " ;
if ( t = = InnerVectorizedTraversal ) return " InnerVectorizedTraversal " ;
if ( t = = LinearVectorizedTraversal ) return " LinearVectorizedTraversal " ;
if ( t = = SliceVectorizedTraversal ) return " SliceVectorizedTraversal " ;
return " ? " ;
}
std : : string demangle_unrolling ( int t )
{
if ( t = = NoUnrolling ) return " NoUnrolling " ;
if ( t = = InnerUnrolling ) return " InnerUnrolling " ;
if ( t = = CompleteUnrolling ) return " CompleteUnrolling " ;
return " ? " ;
}
std : : string demangle_flags ( int f )
{
std : : string res ;
if ( f & RowMajorBit ) res + = " | RowMajor " ;
if ( f & PacketAccessBit ) res + = " | Packet " ;
if ( f & LinearAccessBit ) res + = " | Linear " ;
if ( f & LvalueBit ) res + = " | Lvalue " ;
if ( f & DirectAccessBit ) res + = " | Direct " ;
if ( f & NestByRefBit ) res + = " | NestByRef " ;
if ( f & NoPreferredStorageOrderBit ) res + = " | NoPreferredStorageOrderBit " ;
return res ;
}
# endif
2010-10-25 10:15:22 -04:00
} // end namespace internal
2016-07-18 12:03:05 +02:00
/** \class ScalarBinaryOpTraits
* \ ingroup Core_Module
*
* \ brief Determines whether the given binary operation of two numeric types is allowed and what the scalar return type is .
*
2016-07-20 09:56:39 +02:00
* This class permits to control the scalar return type of any binary operation performed on two different scalar types through ( partial ) template specializations .
*
* For instance , let \ c U1 , \ c U2 and \ c U3 be three user defined scalar types for which most operations between instances of \ c U1 and \ c U2 returns an \ c U3 .
2016-09-06 17:23:15 +02:00
* You can let % Eigen knows that by defining :
2016-07-20 09:56:39 +02:00
\ code
template < typename BinaryOp >
struct ScalarBinaryOpTraits < U1 , U2 , BinaryOp > { typedef U3 ReturnType ; } ;
template < typename BinaryOp >
struct ScalarBinaryOpTraits < U2 , U1 , BinaryOp > { typedef U3 ReturnType ; } ;
\ endcode
* You can then explicitly disable some particular operations to get more explicit error messages :
\ code
template < >
struct ScalarBinaryOpTraits < U1 , U2 , internal : : scalar_max_op < U1 , U2 > > { } ;
\ endcode
* Or customize the return type for individual operation :
\ code
template < >
struct ScalarBinaryOpTraits < U1 , U2 , internal : : scalar_sum_op < U1 , U2 > > { typedef U1 ReturnType ; } ;
\ endcode
*
2016-09-06 17:23:15 +02:00
* By default , the following generic combinations are supported :
< table class = " manual " >
< tr > < th > ScalarA < / th > < th > ScalarB < / th > < th > BinaryOp < / th > < th > ReturnType < / th > < th > Note < / th > < / tr >
< tr > < td > \ c T < / td > < td > \ c T < / td > < td > \ c * < / td > < td > \ c T < / td > < td > < / td > < / tr >
< tr class = " alt " > < td > \ c NumTraits < T > : : Real < / td > < td > \ c T < / td > < td > \ c * < / td > < td > \ c T < / td > < td > Only if \ c NumTraits < T > : : IsComplex < / td > < / tr >
< tr > < td > \ c T < / td > < td > \ c NumTraits < T > : : Real < / td > < td > \ c * < / td > < td > \ c T < / td > < td > Only if \ c NumTraits < T > : : IsComplex < / td > < / tr >
< / table >
*
2016-07-18 12:03:05 +02:00
* \ sa CwiseBinaryOp
*/
template < typename ScalarA , typename ScalarB , typename BinaryOp = internal : : scalar_product_op < ScalarA , ScalarB > >
struct ScalarBinaryOpTraits
# ifndef EIGEN_PARSED_BY_DOXYGEN
// for backward compatibility, use the hints given by the (deprecated) internal::scalar_product_traits class.
: internal : : scalar_product_traits < ScalarA , ScalarB >
# endif // EIGEN_PARSED_BY_DOXYGEN
{ } ;
template < typename T , typename BinaryOp >
struct ScalarBinaryOpTraits < T , T , BinaryOp >
{
typedef T ReturnType ;
} ;
2016-09-06 17:23:15 +02:00
template < typename T , typename BinaryOp >
struct ScalarBinaryOpTraits < T , typename NumTraits < typename internal : : enable_if < NumTraits < T > : : IsComplex , T > : : type > : : Real , BinaryOp >
{
typedef T ReturnType ;
} ;
template < typename T , typename BinaryOp >
struct ScalarBinaryOpTraits < typename NumTraits < typename internal : : enable_if < NumTraits < T > : : IsComplex , T > : : type > : : Real , T , BinaryOp >
{
typedef T ReturnType ;
} ;
2016-07-18 12:03:05 +02:00
// For Matrix * Permutation
template < typename T , typename BinaryOp >
struct ScalarBinaryOpTraits < T , void , BinaryOp >
{
typedef T ReturnType ;
} ;
// For Permutation * Matrix
template < typename T , typename BinaryOp >
struct ScalarBinaryOpTraits < void , T , BinaryOp >
{
typedef T ReturnType ;
} ;
// for Permutation*Permutation
template < typename BinaryOp >
struct ScalarBinaryOpTraits < void , void , BinaryOp >
{
typedef void ReturnType ;
} ;
Relax mixing-type constraints for binary coefficient-wise operators:
- Replace internal::scalar_product_traits<A,B> by Eigen::ScalarBinaryOpTraits<A,B,OP>
- Remove the "functor_is_product_like" helper (was pretty ugly)
- Currently, OP is not used, but it is available to the user for fine grained tuning
- Currently, only the following operators have been generalized: *,/,+,-,=,*=,/=,+=,-=
- TODO: generalize all other binray operators (comparisons,pow,etc.)
- TODO: handle "scalar op array" operators (currently only * is handled)
- TODO: move the handling of the "void" scalar type to ScalarBinaryOpTraits
2016-06-06 15:11:41 +02:00
// We require Lhs and Rhs to have "compatible" scalar types.
2014-02-19 16:30:17 +01:00
// It is tempting to always allow mixing different types but remember that this is often impossible in the vectorized paths.
// So allowing mixing different types gives very unexpected errors when enabling vectorization, when the user tries to
// add together a float matrix and a double matrix.
# define EIGEN_CHECK_BINARY_COMPATIBILIY(BINOP,LHS,RHS) \
2016-07-20 09:56:39 +02:00
EIGEN_STATIC_ASSERT ( ( Eigen : : internal : : has_ReturnType < ScalarBinaryOpTraits < LHS , RHS , BINOP > > : : value ) , \
2014-02-19 16:30:17 +01:00
YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY )
2012-04-15 11:06:28 +01:00
} // end namespace Eigen
2008-08-24 15:15:32 +00:00
# endif // EIGEN_XPRHELPER_H