Coupure prévue mardi 3 Août au matin pour maintenance du serveur. Nous faisons au mieux pour que celle-ci soit la plus brève possible.

Commit 63ca7b83 authored by Sylvain Thery's avatar Sylvain Thery
Browse files

add function //foreach_cell_all_thread & suppression of param nbthread in multi fonctor versions

parent 1f2a45ce
...@@ -170,7 +170,7 @@ typename PFP::REAL totalVolume(typename PFP::MAP& map, const VertexAttribute<typ ...@@ -170,7 +170,7 @@ typename PFP::REAL totalVolume(typename PFP::MAP& map, const VertexAttribute<typ
double total=0.0; double total=0.0;
Algo::Parallel::foreach_cell<typename PFP::MAP,VOLUME>(map, functs, nbth, true, select, current_thread); Algo::Parallel::foreach_cell<typename PFP::MAP,VOLUME>(map, functs, true, select, current_thread);
for (unsigned int i=0; i < nbth; ++i) for (unsigned int i=0; i < nbth; ++i)
{ {
......
...@@ -62,17 +62,51 @@ unsigned int optimalNbThreads( NbParam p=NB_HIGHMEMORY); ...@@ -62,17 +62,51 @@ unsigned int optimalNbThreads( NbParam p=NB_HIGHMEMORY);
void setNbCore(unsigned int nb); void setNbCore(unsigned int nb);
//
//template <typename MAP>
//class Foreach
//{
// MAP& m_map;
//
// std::vector<FunctorMapThreaded<MAP>*> m_funcs;
//
// std::vector<Dart>* m_vd;
//
// unsigned int m_nbth;
//
//public:
// Foreach(MAP& map,unsigned int nbth);
//
// void clearFunctors();
//
// void addFunctor(FunctorMapThreaded<MAP>* funcPtr);
//
// template<typename T>
// T* getFunctor(unsigned int i);
//
// template <unsigned int ORBIT>
// void traverseCell(bool needMarkers = false, const FunctorSelect& good = allDarts, unsigned int currentThread = 0);
//
// template <unsigned int ORBIT>
// void traverseEachCell(bool needMarkers = false, const FunctorSelect& good = allDarts, unsigned int currentThread = 0);
//
// void traverseDart(bool needMarkers = false, const FunctorSelect& good = allDarts, unsigned int currentThread = 0);
//
// void traverseEachDart(bool needMarkers = false, const FunctorSelect& good = allDarts, unsigned int currentThread = 0);
//};
/** /**
* Traverse cells of a map in parallel. Use quick traversal, cell markers or dart markers if available ! * Traverse cells of a map in parallel. Use quick traversal, cell markers or dart markers if available !
* Use this version if you need to have acces to each functors after the traversal (to compute a sum or an average for example) * Use this version if you need to have acces to each functors after the traversal (to compute a sum or an average for example)
* @param map the map * @param map the map
* @param funcs the functors to apply (size of vector must be equal to nbth) * @param funcs the functors to apply (size of vector determine number of threads, and all functors must be of the same type)
* @param nbth number of threads
* @param needMarkers set to yes if you want that each thread use different markers. Warning if set to false (default) do not use algo with thread id or markers !! * @param needMarkers set to yes if you want that each thread use different markers. Warning if set to false (default) do not use algo with thread id or markers !!
* @param good a selector * @param good a selector
*/ */
template <typename MAP, unsigned int ORBIT> template <typename MAP, unsigned int ORBIT>
void foreach_cell(MAP& map, std::vector<FunctorMapThreaded<MAP>*>& funcs, unsigned int nbth, bool needMarkers = false, const FunctorSelect& good = allDarts, unsigned int currentThread = 0); void foreach_cell(MAP& map, std::vector<FunctorMapThreaded<MAP>*>& funcs, bool needMarkers = false, const FunctorSelect& good = allDarts, unsigned int currentThread = 0);
/** /**
* Traverse cells of a map in parallel. Use quick traversal, cell markers or dart markers if available ! * Traverse cells of a map in parallel. Use quick traversal, cell markers or dart markers if available !
...@@ -86,12 +120,25 @@ void foreach_cell(MAP& map, std::vector<FunctorMapThreaded<MAP>*>& funcs, unsign ...@@ -86,12 +120,25 @@ void foreach_cell(MAP& map, std::vector<FunctorMapThreaded<MAP>*>& funcs, unsign
template <typename MAP, unsigned int ORBIT> template <typename MAP, unsigned int ORBIT>
void foreach_cell(MAP& map, FunctorMapThreaded<MAP>& func, unsigned int nbth = 0, bool needMarkers = false, const FunctorSelect& good = allDarts, unsigned int currentThread = 0); void foreach_cell(MAP& map, FunctorMapThreaded<MAP>& func, unsigned int nbth = 0, bool needMarkers = false, const FunctorSelect& good = allDarts, unsigned int currentThread = 0);
/**
* Traverse cells of a map and apply differents functors in //
* Use this version if you need to have acces to each functors after the traversal (to compute a sum or an average for example)
* @param map the map
* @param funcs the functors to apply ( each functors can (should!) be here of different type)
* @param nbth number of threads
* @param needMarkers set to yes if you want that each thread use different markers. Warning if set to false (default) do not use algo with thread id or markers !!
* @param good a selector
*/
template <typename MAP, unsigned int ORBIT>
void foreach_cell_all_thread(MAP& map, std::vector<FunctorMapThreaded<MAP>*>& funcs, bool needMarkers = false, const FunctorSelect& good = allDarts, unsigned int currentThread = 0);
/** /**
* Traverse darts of a map in parallel * Traverse darts of a map in parallel
* Use this version if you need to have acces to each functors after the traversal (to compute a sum or an average for example) * Use this version if you need to have acces to each functors after the traversal (to compute a sum or an average for example)
* @param map the map * @param map the map
* @param funcs the functors to apply * @param funcs the functors to apply (size of vector determine number of threads, and all functors must be of the same type)
* @param nbth number of thread to use
* @param needMarkers set to yes if you want that each thread use different markers.Warning if set to false (default) do not use algo with thread id or markers !! * @param needMarkers set to yes if you want that each thread use different markers.Warning if set to false (default) do not use algo with thread id or markers !!
* @param good a selector * @param good a selector
*/ */
...@@ -115,9 +162,8 @@ void foreach_dart(MAP& map, FunctorMapThreaded<MAP>& func, unsigned int nbth = 0 ...@@ -115,9 +162,8 @@ void foreach_dart(MAP& map, FunctorMapThreaded<MAP>& func, unsigned int nbth = 0
* Traverse all elements of an attribute container (attribute handler is placed in FunctorAttribThreaded) * Traverse all elements of an attribute container (attribute handler is placed in FunctorAttribThreaded)
* @param attr_cont the attribute container to traverse * @param attr_cont the attribute container to traverse
* @param func the fonctors to use * @param func the fonctors to use
* @param nbth number of thread to use for computation
*/ */
void foreach_attrib(AttributeContainer& attr_cont, std::vector<FunctorAttribThreaded*> funcs, unsigned int nbth); void foreach_attrib(AttributeContainer& attr_cont, std::vector<FunctorAttribThreaded*> funcs);
/** /**
* Traverse all elements of an attribute container (attribute handler is placed in FunctorAttribThreaded * Traverse all elements of an attribute container (attribute handler is placed in FunctorAttribThreaded
...@@ -134,12 +180,11 @@ void foreach_attrib(AttributeContainer& attr_cont, FunctorAttribThreaded& func, ...@@ -134,12 +180,11 @@ void foreach_attrib(AttributeContainer& attr_cont, FunctorAttribThreaded& func,
* @param map the map * @param map the map
* @param funcsFrontnBack nbth front pass functors followed by nbth back pass functors * @param funcsFrontnBack nbth front pass functors followed by nbth back pass functors
* @param nbLoops number of loops to execute * @param nbLoops number of loops to execute
* @param nbth number of threads to use
* @param needMarkers set to yes if you want that each thread use different markers (markers are allocated if necessary) * @param needMarkers set to yes if you want that each thread use different markers (markers are allocated if necessary)
* @param good a selector * @param good a selector
*/ */
template <typename MAP, unsigned int CELL> template <typename MAP, unsigned int CELL>
void foreach_cell2Pass(MAP& map, std::vector<FunctorMapThreaded<MAP>*>& funcsFrontnBack, unsigned int nbLoops, unsigned int nbth, bool needMarkers = false, const FunctorSelect& good = allDarts); void foreach_cell2Pass(MAP& map, std::vector<FunctorMapThreaded<MAP>*>& funcsFrontnBack, unsigned int nbLoops, bool needMarkers = false, const FunctorSelect& good = allDarts);
/** /**
* Optimized version for // foreach with to pass (2 functors), with several loops * Optimized version for // foreach with to pass (2 functors), with several loops
......
...@@ -150,7 +150,7 @@ void foreach_cell(MAP& map, FunctorMapThreaded<MAP>& func, unsigned int nbth, bo ...@@ -150,7 +150,7 @@ void foreach_cell(MAP& map, FunctorMapThreaded<MAP>& func, unsigned int nbth, bo
funcs.push_back(func.duplicate()); funcs.push_back(func.duplicate());
} }
foreach_cell<MAP,ORBIT>(map,funcs,nbth,needMarkers,good,currentThread); foreach_cell<MAP,ORBIT>(map,funcs,needMarkers,good,currentThread);
if (!shared) if (!shared)
for (unsigned int i = 0; i < nbth; ++i) for (unsigned int i = 0; i < nbth; ++i)
...@@ -158,9 +158,9 @@ void foreach_cell(MAP& map, FunctorMapThreaded<MAP>& func, unsigned int nbth, bo ...@@ -158,9 +158,9 @@ void foreach_cell(MAP& map, FunctorMapThreaded<MAP>& func, unsigned int nbth, bo
} }
template <typename MAP, unsigned int ORBIT> template <typename MAP, unsigned int ORBIT>
void foreach_cell(MAP& map, std::vector<FunctorMapThreaded<MAP>*>& funcs, unsigned int nbth, bool needMarkers, const FunctorSelect& good, unsigned int currentThread) void foreach_cell(MAP& map, std::vector<FunctorMapThreaded<MAP>*>& funcs, bool needMarkers, const FunctorSelect& good, unsigned int currentThread)
{ {
assert(funcs.size() == nbth); unsigned int nbth = funcs.size();
std::vector<Dart>* vd = new std::vector<Dart>[nbth]; std::vector<Dart>* vd = new std::vector<Dart>[nbth];
boost::thread** threads = new boost::thread*[nbth]; boost::thread** threads = new boost::thread*[nbth];
...@@ -370,7 +370,7 @@ void foreach_dart(MAP& map, FunctorMapThreaded<MAP>& func, unsigned int nbth, bo ...@@ -370,7 +370,7 @@ void foreach_dart(MAP& map, FunctorMapThreaded<MAP>& func, unsigned int nbth, bo
funcs.push_back(func.duplicate()); funcs.push_back(func.duplicate());
} }
foreach_dart<MAP>(map,funcs,nbth,needMarkers,good); foreach_dart<MAP>(map,funcs,needMarkers,good);
if (!shared) if (!shared)
for (unsigned int i = 0; i < nbth; ++i) for (unsigned int i = 0; i < nbth; ++i)
...@@ -379,9 +379,9 @@ void foreach_dart(MAP& map, FunctorMapThreaded<MAP>& func, unsigned int nbth, bo ...@@ -379,9 +379,9 @@ void foreach_dart(MAP& map, FunctorMapThreaded<MAP>& func, unsigned int nbth, bo
template <typename MAP> template <typename MAP>
void foreach_dart(MAP& map, std::vector<FunctorMapThreaded<MAP>*> funcs, unsigned int nbth, bool needMarkers, const FunctorSelect& good) void foreach_dart(MAP& map, std::vector<FunctorMapThreaded<MAP>*> funcs, bool needMarkers, const FunctorSelect& good)
{ {
assert(funcs.size() == nbth); unsigned int nbth = funcs.size();
std::vector<Dart>* vd = new std::vector<Dart>[nbth]; std::vector<Dart>* vd = new std::vector<Dart>[nbth];
boost::thread** threads = new boost::thread*[nbth]; boost::thread** threads = new boost::thread*[nbth];
...@@ -464,11 +464,13 @@ void foreach_dart(MAP& map, std::vector<FunctorMapThreaded<MAP>*> funcs, unsigne ...@@ -464,11 +464,13 @@ void foreach_dart(MAP& map, std::vector<FunctorMapThreaded<MAP>*> funcs, unsigne
} }
// TODO same modification for transparent usage of dart marker / cell marker / quick traversal // TODO same modification for transparent usage of dart marker / cell marker / quick traversal ??
template <typename MAP, unsigned int CELL> template <typename MAP, unsigned int CELL>
void foreach_cell2Pass(MAP& map, std::vector<FunctorMapThreaded<MAP>*>& funcsFrontnBack, unsigned int nbLoops, unsigned int nbth, bool needMarkers, const FunctorSelect& good) void foreach_cell2Pass(MAP& map, std::vector<FunctorMapThreaded<MAP>*>& funcsFrontnBack, unsigned int nbLoops, bool needMarkers, const FunctorSelect& good)
{ {
unsigned int nbth = funcsFrontnBack.size()/2;
std::vector<Dart>* vd = new std::vector<Dart>[2*nbth]; std::vector<Dart>* vd = new std::vector<Dart>[2*nbth];
for (unsigned int i = 0; i < nbth; ++i) for (unsigned int i = 0; i < nbth; ++i)
vd[i].reserve(SIZE_BUFFER_THREAD); vd[i].reserve(SIZE_BUFFER_THREAD);
...@@ -507,13 +509,10 @@ void foreach_cell2Pass(MAP& map, std::vector<FunctorMapThreaded<MAP>*>& funcsFro ...@@ -507,13 +509,10 @@ void foreach_cell2Pass(MAP& map, std::vector<FunctorMapThreaded<MAP>*>& funcsFro
} }
bool finished=false; bool finished=false;
// lauch threads funcsFrontnBack
for (unsigned int i = 0; i < nbth; ++i) for (unsigned int i = 0; i < nbth; ++i)
threadsAB[i] = new boost::thread(ThreadFunction<MAP>(funcsFrontnBack[i], vd[i], sync1, sync2, finished,1+i)); threadsAB[i] = new boost::thread(ThreadFunction<MAP>(funcsFrontnBack[i], vd[i], sync1, sync2, finished,1+i));
// and continue to traverse the map
while (d != map.end()) while (d != map.end())
{ {
for (unsigned int i = 0; i < nbth; ++i) for (unsigned int i = 0; i < nbth; ++i)
...@@ -642,13 +641,415 @@ void foreach_cell2Pass(MAP& map, FunctorMapThreaded<MAP>& funcFront, FunctorMapT ...@@ -642,13 +641,415 @@ void foreach_cell2Pass(MAP& map, FunctorMapThreaded<MAP>& funcFront, FunctorMapT
} }
foreach_cell2Pass<MAP,CELL>(map,funcs,nbLoops,nbth,needMarkers,good); foreach_cell2Pass<MAP,CELL>(map,funcs,nbLoops,needMarkers,good);
if (!shared) if (!shared)
for (unsigned int i = 0; i < 2*nbth; ++i) for (unsigned int i = 0; i < 2*nbth; ++i)
delete funcs[i]; delete funcs[i];
} }
//
//
//template <typename MAP>
//void Foreach<MAP>::Foreach(MAP& map, unsigned int nbth):
//m_nbth(nbth)
//{
// if (m_nbth == 0)
// m_nbth = optimalNbThreads();
//
// m_funcs.reserve(m_nbth);
//
// m_vd = new std::vector<Dart>[2*nbth];
// for (unsigned int i = 0; i < 2*nbth; ++i)
// m_vd[i].reserve(SIZE_BUFFER_THREAD);
//}
//
//template <typename MAP>
//Foreach<MAP>::~Foreach(MAP& map, unsigned int nbth)
//{
// delete[] m_vd;
//}
//
//
//template <typename MAP>
//void Foreach<MAP>:: clearFunctors()
//{
// m_funcs.clear();
//}
//
//template <typename MAP>
//void Foreach<MAP>:: addFunctor(FunctorMapThreaded<MAP>* funcPtr)
//{
// m_funcs.push_back(funcPtr);
//}
//
//template <typename MAP>
//template<typename T>
//T* Foreach<MAP>::getFunctor(unsigned int i)
//{
// assert(i < m_funcs.size());
// return dynamic_cast<T*>(m_funcs[i]);
//}
//
//
//template <typename MAP>
//template <unsigned int ORBIT>
//void Foreach<MAP>::traverseCell<ORBIT>(bool needMarkers, const FunctorSelect& good, unsigned int currentThread)
//{
// assert(m_funcs.size() == m_nbth);
//
// boost::thread** threads = new boost::thread*[m_nbth];
//
// AttributeContainer* cont = NULL;
// DartMarker* dmark = NULL;
// CellMarker<ORBIT>* cmark = NULL;
// AttributeMultiVector<Dart>* quickTraversal = m_map.template getQuickTraversal<ORBIT>() ;
//
// // fill each vd buffers with SIZE_BUFFER_THREAD darts
// Dart d;
// unsigned int di=0;
//
// if(quickTraversal != NULL)
// {
// cont = &(m_map.template getAttributeContainer<ORBIT>()) ;
//
// di = cont->begin();
// unsigned int nb = 0;
// while ((di != cont->end()) && (nb < m_nbth*SIZE_BUFFER_THREAD) )
// {
// d = quickTraversal->operator[](di);
// if (good(d))
// {
// m_vd[nb%m_nbth].push_back(d);
// nb++;
// }
// cont->next(di);
// }
// }
// else
// {
// if(m_map.template isOrbitEmbedded<ORBIT>())
// {
// cmark = new CellMarker<ORBIT>(m_map, currentThread) ;
//
// d = m_map.begin();
// unsigned int nb = 0;
// while ((d != m_map.end()) && (nb < m_nbth*SIZE_BUFFER_THREAD) )
// {
// if (good(d) && (!m_map.isBoundaryMarked(d)) && (!cmark->isMarked(d)))
// {
// cmark->mark(d);
// m_vd[nb%m_nbth].push_back(d);
// nb++;
// }
// m_map.next(d);
// }
// }
// else
// {
// dmark = new DartMarker(m_map, currentThread) ;
// d = m_map.begin();
// unsigned int nb = 0;
// while ((d != m_map.end()) && (nb < m_nbth*SIZE_BUFFER_THREAD) )
// {
// if (good(d) && (!m_map.isBoundaryMarked(d)) && (!dmark->isMarked(d)))
// {
// dmark->markOrbit<ORBIT>(d);
// m_vd[nb%m_nbth].push_back(d);
// nb++;
// }
// m_map.next(d);
// }
// }
// }
//
// boost::barrier sync1(m_nbth+1);
// boost::barrier sync2(m_nbth+1);
// bool finished=false;
// // lauch threads
// if (needMarkers)
// {
// unsigned int nbth_prec = m_map.getNbThreadMarkers();
// if (nbth_prec < m_nbth+1)
// m_map.addThreadMarker(m_nbth+1-nbth_prec);
// }
//
// for (unsigned int i = 0; i < m_nbth; ++i)
// threads[i] = new boost::thread(ThreadFunction<MAP>(m_funcs[i], m_vd[i],sync1,sync2, finished,1+i));
//
//
// if (cont)
// {
// while (di != cont->end())
// {
// for (unsigned int i = 0; i < m_nbth; ++i)
// m_vd[m_nbth+i].clear();
// unsigned int nb = 0;
// while ((di != cont->end()) && (nb < m_nbth*SIZE_BUFFER_THREAD) )
// {
// d = quickTraversal->operator[](di);
// if (good(d))
// {
// m_vd[m_nbth + nb%m_nbth].push_back(d);
// nb++;
// }
// cont->next(di);
// }
// sync1.wait();
// for (unsigned int i = 0; i < m_nbth; ++i)
// m_vd[i].swap(m_vd[m_nbth+i]);
// sync2.wait();
// }
// }
// else if (cmark)
// {
// while (d != m_map.end())
// {
// for (unsigned int i = 0; i < m_nbth; ++i)
// m_vd[m_nbth+i].clear();
// unsigned int nb = 0;
// while ((d != m_map.end()) && (nb < m_nbth*SIZE_BUFFER_THREAD) )
// {
// if (good(d) && (!m_map.isBoundaryMarked(d)) && (!cmark->isMarked(d)))
// {
// cmark->mark(d);
// m_vd[m_nbth+nb%m_nbth].push_back(d);
// nb++;
// }
// m_map.next(d);
// }
// sync1.wait();
// for (unsigned int i = 0; i < m_nbth; ++i)
// m_vd[i].swap(m_vd[m_nbth+i]);
// sync2.wait();
// }
// }
// else
// {
// while (d != m_map.end())
// {
// for (unsigned int i = 0; i < m_nbth; ++i)
// m_vd[m_nbth+i].clear();
// unsigned int nb = 0;
// while ((d != m_map.end()) && (nb < m_nbth*SIZE_BUFFER_THREAD) )
// {
// if (good(d) && (!m_map.isBoundaryMarked(d)) && (!dmark->isMarked(d)))
// {
// dmark->markOrbit<ORBIT>(d);
// m_vd[m_nbth+nb%m_nbth].push_back(d);
// nb++;
// }
// m_map.next(d);
// }
// sync1.wait();
// for (unsigned int i = 0; i < m_nbth; ++i)
// m_vd[i].swap(m_vd[m_nbth+i]);
// sync2.wait();
// }
// }
//
// sync1.wait();
// finished = true;
// sync2.wait();
//
// //wait for all theads to be finished
// for (unsigned int i = 0; i < m_nbth; ++i)
// {
// threads[i]->join();
// delete threads[i];
// }
// delete[] threads;
// delete[] m_vd;
//
// if (cmark != NULL)
// delete cmark;
//
// if (dmark != NULL)
// delete dmark;
//}
//
//
template <typename MAP, unsigned int ORBIT>
void foreach_cell_all_thread(MAP& map, std::vector<FunctorMapThreaded<MAP>*>& funcs, bool needMarkers, const FunctorSelect& good, unsigned int currentThread)
{
unsigned int nbth = funcs.size();
boost::thread** threads = new boost::thread*[nbth];
std::vector<Dart> vd;
vd.reserve(SIZE_BUFFER_THREAD);
AttributeContainer* cont = NULL;
DartMarker* dmark = NULL;
CellMarker<ORBIT>* cmark = NULL;
AttributeMultiVector<Dart>* quickTraversal = map.template getQuickTraversal<ORBIT>() ;
// fill each vd buffers with SIZE_BUFFER_THREAD darts
Dart d;
unsigned int di=0;
if(quickTraversal != NULL)
{
cont = &(map.template getAttributeContainer<ORBIT>()) ;
di = cont->begin();
unsigned int nb = 0;
while ((di != cont->end()) && (nb < SIZE_BUFFER_THREAD) )
{
d = quickTraversal->operator[](di);
if (good(d))
{
vd.push_back(d);
nb++;
}
cont->next(di);
}
}
else
{
if(map.template isOrbitEmbedded<ORBIT>())
{
cmark = new CellMarker<ORBIT>(map, currentThread) ;
d = map.begin();
unsigned int nb=0;
while ((d != map.end()) && (nb < SIZE_BUFFER_THREAD) )
{
if (good(d) && (!map.isBoundaryMarked(d)) && (!cmark->isMarked(d)))
{
cmark->mark(d);
vd.push_back(d);
nb++;
}
map.next(d);
}
}
else
{
dmark = new DartMarker(map, currentThread) ;
d = map.begin();
unsigned int nb=0;
while ((d != map.end()) && (nb < SIZE_BUFFER_THREAD) )
{
if (good(d) && (!map.isBoundaryMarked(d)) && (!dmark->isMarked(d)))
{
dmark->markOrbit<ORBIT>(d);
vd.push_back(d);
nb++;
}
map.next(d);
}
}
}
boost::barrier sync1(nbth+1);
boost::barrier sync2(nbth+1);
bool finished=false;
// lauch threads
if (needMarkers)
{
unsigned int nbth_prec = map.getNbThreadMarkers();
if (nbth_prec < nbth+1)
map.addThreadMarker(nbth+1-nbth_prec);
}
for (unsigned int i = 0; i < nbth; ++i)
threads[i] = new boost::thread(ThreadFunction<MAP>(funcs[i], vd,sync1,sync2, finished,1+i));
// and continue to traverse the map
std::vector<Dart> tempo;
tempo.reserve(SIZE_BUFFER_THREAD);
if (cont)
{
while (di != cont->end())
{
tempo.clear();
unsigned int nb=0;
while ((di != cont->end()) && (nb < SIZE_BUFFER_THREAD) )
{
d = quickTraversal->operator[](di);
if (good(d))
{
tempo.push_back(d);
nb++;
}
cont->next(di);
}
sync1.wait();
vd.swap(tempo);