Compare commits

...

15 Commits
3.3.8 ... 3.3.9

Author SHA1 Message Date
David Tellenbach
0fd6b4f71d Bump to 3.3.9 2020-12-04 22:53:41 +01:00
Florian Maurin
52207cf6f9 Fix typo in doc
(cherry picked from commit c5985c46f5)
2020-11-30 12:09:43 +00:00
Jim Lersch
0c26611d2d Workaround for doxygen class template titles in which the template
part of the class signature is lost due to a problem with forward
declarations.  The problem is probably caused by doxygen bug #7689.
It is confirmed to be fixed in doxygen >= 1.8.19.

(cherry picked from commit 68f69414f7)
2020-11-28 16:21:12 +01:00
Christoph Hertzberg
2a4fcb2c31 Fix doxygen class block that was wrongly named.
Manually cherry-picked from a7170f2aca
2020-11-27 19:41:19 +01:00
Christoph Hertzberg
54930b6b55 Remove unused variable 2020-11-25 17:59:18 +01:00
Martin Vonheim Larsen
4e5385c905 Enable MathJax in Doxygen.in
Note that HTTPS must be used against the MathJax CDN when hosted on `eigen.tuxfamily.org` (which uses HTTPS) in order to avoid `Mixed Content`-errors from browsers. Using HTTPS for MathJax also works if the Eigen docs are hosted on plain HTTP.

(cherry picked from commit 280f4f2407)
2020-11-17 15:39:44 +01:00
Christoph Hertzberg
ac632f663e bug #1746: Removed implementation of standard copy-constructor and standard copy-assign-operator from PermutationMatrix and Transpositions to allow malloc-less std::move. Added unit-test to rvalue_types
(cherry picked from commit efd9867ff0)
2020-11-12 11:54:51 +01:00
Christoph Hertzberg
3620371c5c Bug #2036 make sure find_standard_math_library_test_program actually compiles (and is guaranteed to call math functions)
(cherry picked from commit ecb7bc9514)
2020-11-04 13:38:17 +01:00
David Tellenbach
5dda502f84 Rename test/array.cpp to test/array_cwise.cpp
Having a test named "array" can clash with the standard library header
"array".

Fixes issue #2046
2020-11-04 13:01:17 +01:00
Gael Guennebaud
590aec8fab check two ctors
(cherry picked from commit 572d62697d)
2020-10-28 09:53:23 +01:00
David Tellenbach
75f8b06e50 Mention problems when using potentially throwing scalars and OpenMP
(cherry picked from commit 9022f5aa8a)
2020-10-09 17:41:41 +02:00
Karl Ljungkvist
e91e5d8c87 Fix typo in Tutorial_BlockOperations_block_assignment.cpp
(cherry picked from commit d199c17b14)
2020-10-09 14:19:23 +02:00
Luke Peterson
ef3cc72cb6 Remove error counting in OpenMP parallelize_gemm
This resolves a compilation error associated with
Eigen::eigen_assert_exception. It also eliminates the counting of
exceptions that may occur in the OpenMP parallel section. If an
unhandled exception occurs in this section, the behavior is non-conforming
according to the OpenMP specification.
2020-10-08 18:50:33 -07:00
David Tellenbach
7a0a2a5001 Define coeff-wise binary array operators for base class
This fixes #2012.
2020-10-09 00:53:34 +02:00
szczepaniak bartek
bfdd4a9903 Fix Paradiso.
EIGEN_USING_STD -> EIGEN_USING_STD_MATH
2020-10-08 19:38:35 +00:00
18 changed files with 115 additions and 115 deletions

View File

@@ -87,17 +87,6 @@ class PermutationBase : public EigenBase<Derived>
return derived();
}
#ifndef EIGEN_PARSED_BY_DOXYGEN
/** This is a special case of the templated operator=. Its purpose is to
* prevent a default operator= from hiding the templated operator=.
*/
Derived& operator=(const PermutationBase& other)
{
indices() = other.indices();
return derived();
}
#endif
/** \returns the number of rows */
inline Index rows() const { return Index(indices().size()); }
@@ -333,12 +322,6 @@ class PermutationMatrix : public PermutationBase<PermutationMatrix<SizeAtCompile
inline PermutationMatrix(const PermutationBase<OtherDerived>& other)
: m_indices(other.indices()) {}
#ifndef EIGEN_PARSED_BY_DOXYGEN
/** Standard copy constructor. Defined only to prevent a default copy constructor
* from hiding the other templated constructor */
inline PermutationMatrix(const PermutationMatrix& other) : m_indices(other.indices()) {}
#endif
/** Generic constructor from expression of the indices. The indices
* array has the meaning that the permutations sends each integer i to indices[i].
*
@@ -373,17 +356,6 @@ class PermutationMatrix : public PermutationBase<PermutationMatrix<SizeAtCompile
return Base::operator=(tr.derived());
}
#ifndef EIGEN_PARSED_BY_DOXYGEN
/** This is a special case of the templated operator=. Its purpose is to
* prevent a default operator= from hiding the templated operator=.
*/
PermutationMatrix& operator=(const PermutationMatrix& other)
{
m_indices = other.m_indices;
return *this;
}
#endif
/** const version of indices(). */
const IndicesType& indices() const { return m_indices; }
/** \returns a reference to the stored array representing the permutation. */

View File

@@ -33,17 +33,6 @@ class TranspositionsBase
indices() = other.indices();
return derived();
}
#ifndef EIGEN_PARSED_BY_DOXYGEN
/** This is a special case of the templated operator=. Its purpose is to
* prevent a default operator= from hiding the templated operator=.
*/
Derived& operator=(const TranspositionsBase& other)
{
indices() = other.indices();
return derived();
}
#endif
/** \returns the number of transpositions */
Index size() const { return indices().size(); }
@@ -171,12 +160,6 @@ class Transpositions : public TranspositionsBase<Transpositions<SizeAtCompileTim
inline Transpositions(const TranspositionsBase<OtherDerived>& other)
: m_indices(other.indices()) {}
#ifndef EIGEN_PARSED_BY_DOXYGEN
/** Standard copy constructor. Defined only to prevent a default copy constructor
* from hiding the other templated constructor */
inline Transpositions(const Transpositions& other) : m_indices(other.indices()) {}
#endif
/** Generic constructor from expression of the transposition indices. */
template<typename Other>
explicit inline Transpositions(const MatrixBase<Other>& indices) : m_indices(indices)
@@ -189,17 +172,6 @@ class Transpositions : public TranspositionsBase<Transpositions<SizeAtCompileTim
return Base::operator=(other);
}
#ifndef EIGEN_PARSED_BY_DOXYGEN
/** This is a special case of the templated operator=. Its purpose is to
* prevent a default operator= from hiding the templated operator=.
*/
Transpositions& operator=(const Transpositions& other)
{
m_indices = other.m_indices;
return *this;
}
#endif
/** Constructs an uninitialized permutation matrix of given size.
*/
inline Transpositions(Index size) : m_indices(size)
@@ -306,17 +278,6 @@ class TranspositionsWrapper
return Base::operator=(other);
}
#ifndef EIGEN_PARSED_BY_DOXYGEN
/** This is a special case of the templated operator=. Its purpose is to
* prevent a default operator= from hiding the templated operator=.
*/
TranspositionsWrapper& operator=(const TranspositionsWrapper& other)
{
m_indices = other.m_indices;
return *this;
}
#endif
/** const version of indices(). */
const IndicesType& indices() const { return m_indices; }

View File

@@ -132,8 +132,7 @@ void parallelize_gemm(const Functor& func, Index rows, Index cols, Index depth,
ei_declare_aligned_stack_constructed_variable(GemmParallelInfo<Index>,info,threads,0);
int errorCount = 0;
#pragma omp parallel num_threads(threads) reduction(+: errorCount)
#pragma omp parallel num_threads(threads)
{
Index i = omp_get_thread_num();
// Note that the actual number of threads might be lower than the number of request ones.
@@ -152,14 +151,11 @@ void parallelize_gemm(const Functor& func, Index rows, Index cols, Index depth,
info[i].lhs_start = r0;
info[i].lhs_length = actualBlockRows;
EIGEN_TRY {
if(transpose) func(c0, actualBlockCols, 0, rows, info);
else func(0, rows, c0, actualBlockCols, info);
} EIGEN_CATCH(...) {
++errorCount;
}
if(transpose)
func(c0, actualBlockCols, 0, rows, info);
else
func(0, rows, c0, actualBlockCols, info);
}
if (errorCount) EIGEN_THROW_X(Eigen::eigen_assert_exception());
#endif
}

View File

@@ -13,7 +13,7 @@
#define EIGEN_WORLD_VERSION 3
#define EIGEN_MAJOR_VERSION 3
#define EIGEN_MINOR_VERSION 8
#define EIGEN_MINOR_VERSION 9
#define EIGEN_VERSION_AT_LEAST(x,y,z) (EIGEN_WORLD_VERSION>x || (EIGEN_WORLD_VERSION>=x && \
(EIGEN_MAJOR_VERSION>y || (EIGEN_MAJOR_VERSION>=y && \

2
Eigen/src/Geometry/Scaling.h Executable file → Normal file
View File

@@ -14,7 +14,7 @@ namespace Eigen {
/** \geometry_module \ingroup Geometry_Module
*
* \class Scaling
* \class UniformScaling
*
* \brief Represents a generic uniform scaling transformation
*

View File

@@ -192,7 +192,7 @@ class PardisoImpl : public SparseSolverBase<Derived>
void pardisoInit(int type)
{
m_type = type;
EIGEN_USING_STD(abs);
EIGEN_USING_STD_MATH(abs);
bool symmetric = abs(m_type) < 10;
m_iparm[0] = 1; // No solver default
m_iparm[1] = 2; // use Metis for the ordering

View File

@@ -119,7 +119,7 @@ OP(const Scalar& s) const { \
return this->OP(Derived::PlainObject::Constant(rows(), cols(), s)); \
} \
EIGEN_DEVICE_FUNC friend EIGEN_STRONG_INLINE const RCmp ## COMPARATOR ## ReturnType \
OP(const Scalar& s, const Derived& d) { \
OP(const Scalar& s, const EIGEN_CURRENT_STORAGE_BASE_CLASS<Derived>& d) { \
return Derived::PlainObject::Constant(d.rows(), d.cols(), s).OP(d); \
}

View File

@@ -19,8 +19,11 @@ include(CheckCXXSourceCompiles)
# notice the std:: is required on some platforms such as QNX
set(find_standard_math_library_test_program
"#include<cmath>
int main() { std::sin(0.0); std::log(0.0f); }")
"
#include<cmath>
int main(int argc, char **){
return int(std::sin(double(argc)) + std::log(double(argc)));
}")
# first try compiling/linking the test program without any linker flags

View File

@@ -11,7 +11,7 @@ if(CMAKE_COMPILER_IS_GNUCXX)
endif(CMAKE_COMPILER_IS_GNUCXX)
option(EIGEN_INTERNAL_DOCUMENTATION "Build internal documentation" OFF)
option(EIGEN_DOC_USE_MATHJAX "Use MathJax for rendering math in HTML docs" ON)
# Set some Doxygen flags
set(EIGEN_DOXY_PROJECT_NAME "Eigen")
@@ -19,12 +19,19 @@ set(EIGEN_DOXY_OUTPUT_DIRECTORY_SUFFIX "")
set(EIGEN_DOXY_INPUT "\"${Eigen_SOURCE_DIR}/Eigen\" \"${Eigen_SOURCE_DIR}/doc\"")
set(EIGEN_DOXY_HTML_COLORSTYLE_HUE "220")
set(EIGEN_DOXY_TAGFILES "")
if(EIGEN_INTERNAL_DOCUMENTATION)
set(EIGEN_DOXY_INTERNAL "YES")
else(EIGEN_INTERNAL_DOCUMENTATION)
set(EIGEN_DOXY_INTERNAL "NO")
endif(EIGEN_INTERNAL_DOCUMENTATION)
if (EIGEN_DOC_USE_MATHJAX)
set(EIGEN_DOXY_USE_MATHJAX "YES")
else ()
set(EIGEN_DOXY_USE_MATHJAX "NO")
endif()
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in
${CMAKE_CURRENT_BINARY_DIR}/Doxyfile

View File

@@ -736,6 +736,14 @@ EXCLUDE = "${Eigen_SOURCE_DIR}/Eigen/src/Core/products" \
"${Eigen_SOURCE_DIR}/unsupported/doc/examples" \
"${Eigen_SOURCE_DIR}/unsupported/doc/snippets"
# Forward declarations of class templates cause the title of the main page for
# the class template to not contain the template signature. This only happens
# when the \class command is used to document the class. Possibly caused
# by https://github.com/doxygen/doxygen/issues/7698. Confirmed fixed by
# doxygen release 1.8.19.
EXCLUDE += "${Eigen_SOURCE_DIR}/Eigen/src/Core/util/ForwardDeclarations.h"
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
# from the input.
@@ -1245,7 +1253,7 @@ FORMULA_TRANSPARENT = YES
# output. When enabled you may also need to install MathJax separately and
# configure the path to it using the MATHJAX_RELPATH option.
USE_MATHJAX = NO
USE_MATHJAX = @EIGEN_DOXY_USE_MATHJAX@
# When MathJax is enabled you need to specify the location relative to the
# HTML output directory using the MATHJAX_RELPATH option. The destination
@@ -1257,12 +1265,12 @@ USE_MATHJAX = NO
# However, it is strongly recommended to install a local
# copy of MathJax from http://www.mathjax.org before deployment.
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
MATHJAX_RELPATH = https://cdn.mathjax.org/mathjax/latest
# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
# names that should be enabled during MathJax rendering.
MATHJAX_EXTENSIONS =
MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
# When the SEARCHENGINE tag is enabled doxygen will generate a search box
# for the HTML output. The underlying search engine uses javascript

View File

@@ -244,7 +244,7 @@ As stated earlier, for a read-write sub-matrix (RW), the evaluation can be done
<td>
\code
sm1.valuePtr(); // Pointer to the values
sm1.innerIndextr(); // Pointer to the indices.
sm1.innerIndexPtr(); // Pointer to the indices.
sm1.outerIndexPtr(); // Pointer to the beginning of each inner vector
\endcode
</td>

View File

@@ -49,6 +49,7 @@ int main(int argc, char** argv)
In the case your application is parallelized with OpenMP, you might want to disable Eigen's own parallization as detailed in the previous section.
\warning Using OpenMP with custom scalar types that might throw exceptions can lead to unexpected behaviour in the event of throwing.
*/
}

View File

@@ -14,5 +14,5 @@ int main()
a.block<2,2>(1,1) = m;
cout << "Here is now a with m copied into its central 2x2 block:" << endl << a << endl << endl;
a.block(0,0,2,3) = a.block(2,1,2,3);
cout << "Here is now a with bottom-right 2x3 block copied into top-left 2x2 block:" << endl << a << endl << endl;
cout << "Here is now a with bottom-right 2x3 block copied into top-left 2x3 block:" << endl << a << endl << endl;
}

View File

@@ -163,7 +163,7 @@ ei_add_test(constructor)
ei_add_test(linearstructure)
ei_add_test(integer_types)
ei_add_test(unalignedcount)
if(NOT EIGEN_TEST_NO_EXCEPTIONS)
if(NOT EIGEN_TEST_NO_EXCEPTIONS AND NOT EIGEN_TEST_OPENMP)
ei_add_test(exceptions)
endif()
ei_add_test(redux)
@@ -185,7 +185,7 @@ ei_add_test(smallvectors)
ei_add_test(mapped_matrix)
ei_add_test(mapstride)
ei_add_test(mapstaticmethods)
ei_add_test(array)
ei_add_test(array_cwise)
ei_add_test(array_for_matrix)
ei_add_test(array_replicate)
ei_add_test(array_reverse)

View File

@@ -446,7 +446,7 @@ template<typename ArrayType> void min_max(const ArrayType& m)
}
void test_array()
void test_array_cwise()
{
for(int i = 0; i < g_repeat; i++) {
CALL_SUBTEST_1( array(Array<float, 1, 1>()) );

View File

@@ -8,7 +8,7 @@ struct Foo
static Index object_limit;
int dummy;
Foo()
Foo() : dummy(0)
{
#ifdef EIGEN_EXCEPTIONS
// TODO: Is this the correct way to handle this?
@@ -37,22 +37,33 @@ void test_ctorleak()
{
typedef Matrix<Foo, Dynamic, Dynamic> MatrixX;
typedef Matrix<Foo, Dynamic, 1> VectorX;
Foo::object_count = 0;
for(int i = 0; i < g_repeat; i++) {
Index rows = internal::random<Index>(2,EIGEN_TEST_MAX_SIZE), cols = internal::random<Index>(2,EIGEN_TEST_MAX_SIZE);
Foo::object_limit = internal::random<Index>(0, rows*cols - 2);
Foo::object_limit = rows*cols;
{
MatrixX r(rows, cols);
Foo::object_limit = r.size()+internal::random<Index>(0, rows*cols - 2);
std::cout << "object_limit =" << Foo::object_limit << std::endl;
#ifdef EIGEN_EXCEPTIONS
try
{
#endif
std::cout << "\nMatrixX m(" << rows << ", " << cols << ");\n";
MatrixX m(rows, cols);
if(internal::random<bool>()) {
std::cout << "\nMatrixX m(" << rows << ", " << cols << ");\n";
MatrixX m(rows, cols);
}
else {
std::cout << "\nMatrixX m(r);\n";
MatrixX m(r);
}
#ifdef EIGEN_EXCEPTIONS
VERIFY(false); // not reached if exceptions are enabled
}
catch (const Foo::Fail&) { /* ignore */ }
#endif
}
VERIFY_IS_EQUAL(Index(0), Foo::object_count);
{
@@ -66,4 +77,5 @@ void test_ctorleak()
}
VERIFY_IS_EQUAL(Index(0), Foo::object_count);
}
std::cout << "\n";
}

View File

@@ -16,12 +16,6 @@
#endif
// using namespace Eigen;
#ifdef EIGEN_VECTORIZE_SSE
const bool g_vectorize_sse = true;
#else
const bool g_vectorize_sse = false;
#endif
namespace Eigen {
namespace internal {
template<typename T> T negate(const T& x) { return -x; }

View File

@@ -7,6 +7,8 @@
// 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/.
#define EIGEN_RUNTIME_NO_MALLOC
#include "main.h"
#include <Eigen/Core>
@@ -24,41 +26,85 @@ void rvalue_copyassign(const MatrixType& m)
MatrixType tmp = m;
UIntPtr src_address = reinterpret_cast<UIntPtr>(tmp.data());
Eigen::internal::set_is_malloc_allowed(false); // moving from an rvalue reference shall never allocate
// move the temporary to n
MatrixType n = std::move(tmp);
UIntPtr dst_address = reinterpret_cast<UIntPtr>(n.data());
if (MatrixType::RowsAtCompileTime==Dynamic|| MatrixType::ColsAtCompileTime==Dynamic)
{
// verify that we actually moved the guts
VERIFY_IS_EQUAL(src_address, dst_address);
VERIFY_IS_EQUAL(tmp.size(), 0);
VERIFY_IS_EQUAL(reinterpret_cast<UIntPtr>(tmp.data()), UIntPtr(0));
}
// verify that the content did not change
Scalar abs_diff = (m-n).array().abs().sum();
VERIFY_IS_EQUAL(abs_diff, Scalar(0));
Eigen::internal::set_is_malloc_allowed(true);
}
template<typename TranspositionsType>
void rvalue_transpositions(Index rows)
{
typedef typename TranspositionsType::IndicesType PermutationVectorType;
PermutationVectorType vec;
randomPermutationVector(vec, rows);
TranspositionsType t0(vec);
Eigen::internal::set_is_malloc_allowed(false); // moving from an rvalue reference shall never allocate
UIntPtr t0_address = reinterpret_cast<UIntPtr>(t0.indices().data());
// Move constructors:
TranspositionsType t1 = std::move(t0);
UIntPtr t1_address = reinterpret_cast<UIntPtr>(t1.indices().data());
VERIFY_IS_EQUAL(t0_address, t1_address);
// t0 must be de-allocated:
VERIFY_IS_EQUAL(t0.size(), 0);
VERIFY_IS_EQUAL(reinterpret_cast<UIntPtr>(t0.indices().data()), UIntPtr(0));
// Move assignment:
t0 = std::move(t1);
t0_address = reinterpret_cast<UIntPtr>(t0.indices().data());
VERIFY_IS_EQUAL(t0_address, t1_address);
// t1 must be de-allocated:
VERIFY_IS_EQUAL(t1.size(), 0);
VERIFY_IS_EQUAL(reinterpret_cast<UIntPtr>(t1.indices().data()), UIntPtr(0));
Eigen::internal::set_is_malloc_allowed(true);
}
#else
template <typename MatrixType>
void rvalue_copyassign(const MatrixType&) {}
template<typename TranspositionsType>
void rvalue_transpositions(Index) {}
#endif
void test_rvalue_types()
{
CALL_SUBTEST_1(rvalue_copyassign( MatrixXf::Random(50,50).eval() ));
CALL_SUBTEST_1(rvalue_copyassign( ArrayXXf::Random(50,50).eval() ));
for(int i = 0; i < g_repeat; i++) {
CALL_SUBTEST_1(rvalue_copyassign( MatrixXf::Random(50,50).eval() ));
CALL_SUBTEST_1(rvalue_copyassign( ArrayXXf::Random(50,50).eval() ));
CALL_SUBTEST_1(rvalue_copyassign( Matrix<float,1,Dynamic>::Random(50).eval() ));
CALL_SUBTEST_1(rvalue_copyassign( Array<float,1,Dynamic>::Random(50).eval() ));
CALL_SUBTEST_1(rvalue_copyassign( Matrix<float,1,Dynamic>::Random(50).eval() ));
CALL_SUBTEST_1(rvalue_copyassign( Array<float,1,Dynamic>::Random(50).eval() ));
CALL_SUBTEST_1(rvalue_copyassign( Matrix<float,Dynamic,1>::Random(50).eval() ));
CALL_SUBTEST_1(rvalue_copyassign( Array<float,Dynamic,1>::Random(50).eval() ));
CALL_SUBTEST_1(rvalue_copyassign( Matrix<float,Dynamic,1>::Random(50).eval() ));
CALL_SUBTEST_1(rvalue_copyassign( Array<float,Dynamic,1>::Random(50).eval() ));
CALL_SUBTEST_2(rvalue_copyassign( Array<float,2,1>::Random().eval() ));
CALL_SUBTEST_2(rvalue_copyassign( Array<float,3,1>::Random().eval() ));
CALL_SUBTEST_2(rvalue_copyassign( Array<float,4,1>::Random().eval() ));
CALL_SUBTEST_2(rvalue_copyassign( Array<float,2,2>::Random().eval() ));
CALL_SUBTEST_2(rvalue_copyassign( Array<float,3,3>::Random().eval() ));
CALL_SUBTEST_2(rvalue_copyassign( Array<float,4,4>::Random().eval() ));
CALL_SUBTEST_2(rvalue_copyassign( Array<float,2,1>::Random().eval() ));
CALL_SUBTEST_2(rvalue_copyassign( Array<float,3,1>::Random().eval() ));
CALL_SUBTEST_2(rvalue_copyassign( Array<float,4,1>::Random().eval() ));
CALL_SUBTEST_2(rvalue_copyassign( Array<float,2,2>::Random().eval() ));
CALL_SUBTEST_2(rvalue_copyassign( Array<float,3,3>::Random().eval() ));
CALL_SUBTEST_2(rvalue_copyassign( Array<float,4,4>::Random().eval() ));
CALL_SUBTEST_3((rvalue_transpositions<PermutationMatrix<Dynamic, Dynamic, int> >(internal::random<int>(1,EIGEN_TEST_MAX_SIZE))));
CALL_SUBTEST_3((rvalue_transpositions<PermutationMatrix<Dynamic, Dynamic, Index> >(internal::random<int>(1,EIGEN_TEST_MAX_SIZE))));
CALL_SUBTEST_4((rvalue_transpositions<Transpositions<Dynamic, Dynamic, int> >(internal::random<int>(1,EIGEN_TEST_MAX_SIZE))));
CALL_SUBTEST_4((rvalue_transpositions<Transpositions<Dynamic, Dynamic, Index> >(internal::random<int>(1,EIGEN_TEST_MAX_SIZE))));
}
}