From 749d5d2490db9650e0331c00eff9eede9a7ff25c Mon Sep 17 00:00:00 2001 From: Kirill Terekhov Date: Thu, 6 Oct 2016 10:18:41 -0700 Subject: [PATCH] Nonlocal ghost enumeration mapping for RowMerger RowMerger can now remap global indices that do not belong to initial interval span into local indices and back. This leads to important memory optimization in Automatizator, which now do not require RowMerger interval to span over entire range of global indices on each processor, but require only a small extension. Added calculation of nonlocal indices of ghost elements in Automatizator::EnumerateDynamicTags Fixed a warning in mesh_ecl_file.cpp Fixed SVG output in OldDrawGrid example that was not sorting correctly the faces, leaded to visual artifacts. --- Examples/OldDrawGrid/main.cpp | 2 + Source/Autodiff/autodiff.cpp | 88 ++++++++++++++++++- Source/Headers/inmost_sparse.h | 77 +++++++++++++---- Source/IO/mesh_ecl_file.cpp | 2 +- Source/Solver/sparse.cpp | 151 ++++++++++++++++++++++++++------- 5 files changed, 267 insertions(+), 53 deletions(-) diff --git a/Examples/OldDrawGrid/main.cpp b/Examples/OldDrawGrid/main.cpp index 9517ff4..933e61d 100644 --- a/Examples/OldDrawGrid/main.cpp +++ b/Examples/OldDrawGrid/main.cpp @@ -4785,6 +4785,8 @@ void svg_draw(std::ostream & file) //svg_draw_faces(file,temp_boundary,modelview,projection,viewport); std::vector sorted_clip_boundary(clip_boundary); + for(INMOST_DATA_ENUM_TYPE q = 0; q < sorted_clip_boundary.size() ; q++) + sorted_clip_boundary[q].compute_dist(campos); face2gl::radix_sort_dist(sorted_clip_boundary); svg_draw_faces(file,sorted_clip_boundary,modelview,projection,viewport); diff --git a/Source/Autodiff/autodiff.cpp b/Source/Autodiff/autodiff.cpp index a296e48..5d2e38a 100644 --- a/Source/Autodiff/autodiff.cpp +++ b/Source/Autodiff/autodiff.cpp @@ -157,6 +157,7 @@ namespace INMOST } } } + std::set Pre, Post; //Nonlocal indices #if defined(USE_MPI) if (m->GetProcessorsNumber() > 1) { @@ -228,18 +229,99 @@ namespace INMOST } } } + //synchronize indices last_num += first_num; { std::vector exch_tags; for (index_enum::iterator it = index_tags.begin(); it != index_tags.end(); ++it) exch_tags.push_back(it->indices); m->ExchangeData(exch_tags, exch_mask,0); } + //compute out-of-bounds indices + for (index_enum::iterator it = index_tags.begin(); it != index_tags.end(); ++it) + { + for (ElementType etype = NODE; etype <= MESH; etype = etype << 1) + { + if (it->indices.isDefined(etype)) + { + exch_mask |= etype; + if (it->indices.GetSize() == ENUMUNDEF) + { + if (!it->indices.isSparse(etype)) + { + for (Mesh::iteratorStorage jt = m->Begin(etype); jt != m->End(); ++jt) + { + if ((!(etype & paralleltypes) || ((etype & paralleltypes) && jt->getAsElement()->GetStatus() == Element::Ghost)) && (it->d.domain_mask == 0 || jt->GetMarker(it->d.domain_mask))) + { + Storage::integer_array indarr = jt->IntegerArray(it->indices); + for (Storage::integer_array::iterator qt = indarr.begin(); qt != indarr.end(); ++qt) + { + if( *qt < first_num ) Pre.insert(*qt); + else if( *qt >= last_num ) Post.insert(*qt); + } + } + } + } + else + { + for (Mesh::iteratorStorage jt = m->Begin(etype); jt != m->End(); ++jt) + { + if ((!(etype & paralleltypes) || ((etype & paralleltypes) && jt->getAsElement()->GetStatus() == Element::Ghost)) && (jt->HaveData(it->d.t) && (it->d.domain_mask == 0 || jt->GetMarker(it->d.domain_mask)))) + { + Storage::integer_array indarr = jt->IntegerArray(it->indices); + indarr.resize(jt->RealArray(it->d.t).size()); + for (Storage::integer_array::iterator qt = indarr.begin(); qt != indarr.end(); ++qt) + { + if( *qt < first_num ) Pre.insert(*qt); + else if( *qt >= last_num ) Post.insert(*qt); + } + } + } + } + } + else //getsize + { + if (!it->indices.isSparse(etype)) + { + for (Mesh::iteratorStorage jt = m->Begin(etype); jt != m->End(); ++jt) + { + if ((!(etype & paralleltypes) || ((etype & paralleltypes) && jt->getAsElement()->GetStatus() == Element::Ghost)) && (it->d.domain_mask == 0 || jt->GetMarker(it->d.domain_mask))) + { + Storage::integer_array indarr = jt->IntegerArray(it->indices); + for (Storage::integer_array::iterator qt = indarr.begin(); qt != indarr.end(); ++qt) + { + if( *qt < first_num ) Pre.insert(*qt); + else if( *qt >= last_num ) Post.insert(*qt); + } + } + } + } + else + { + for (Mesh::iteratorStorage jt = m->Begin(etype); jt != m->End(); ++jt) + { + if ((!(etype & paralleltypes) || ((etype & paralleltypes) && jt->getAsElement()->GetStatus() == Element::Ghost)) && (jt->HaveData(it->d.t) && (it->d.domain_mask == 0 || jt->GetMarker(it->d.domain_mask)))) + { + Storage::integer_array indarr = jt->IntegerArray(it->indices); + for (Storage::integer_array::iterator qt = indarr.begin(); qt != indarr.end(); ++qt) + { + if( *qt < first_num ) Pre.insert(*qt); + else if( *qt >= last_num ) Post.insert(*qt); + } + } + } + } + } //getsize + } //isdefined + } //etype + } //it + // after cycle } #endif // this version will fail in parallel //merger.Resize(first_num,last_num,false); // use this version until there is a way to define multiple intervals in RowMerger - INMOST_DATA_INTEGER_TYPE max_unknowns = m->AggregateMax(static_cast(last_num)); + //INMOST_DATA_INTEGER_TYPE max_unknowns = m->AggregateMax(static_cast(last_num)); + //std::cout << "Proc " << m->GetProcessorRank() << " size " << last_num-first_num << " pre " << Pre.size() << " post " << Post.size() << " max " << max_unknowns << std::endl; #if defined(USE_OMP) #pragma omp parallel { @@ -247,10 +329,10 @@ namespace INMOST { merger.resize(omp_get_num_procs()); } - merger[omp_get_thread_num()].Resize(0,max_unknowns,false); + merger[omp_get_thread_num()].Resize(first_num,last_num,std::vector(Pre.begin(),Pre.end()),std::vector(Post.begin(),Post.end()),false); } #else - merger.Resize(0,max_unknowns,false); + merger.Resize(first_num,last_num,std::vector(Pre.begin(),Pre.end()),std::vector(Post.begin(),Post.end()),false); #endif } #endif //USE_MESH diff --git a/Source/Headers/inmost_sparse.h b/Source/Headers/inmost_sparse.h index f4c8ccd..93b5d8f 100644 --- a/Source/Headers/inmost_sparse.h +++ b/Source/Headers/inmost_sparse.h @@ -503,8 +503,7 @@ namespace INMOST #endif //defined(USE_SOLVER) #if defined(USE_SOLVER) || defined(USE_AUTODIFF) - - /// This class may be used to sum multiple sparse rows. + /// This class may be used to sum multiple sparse rows. /// \warning /// In parallel column indices of the matrix may span wider then /// local row indices, to prevent any problem you are currently @@ -550,8 +549,26 @@ namespace INMOST private: bool Sorted; ///< Contents of linked list should be sorted. INMOST_DATA_ENUM_TYPE Nonzeros; ///< Number of nonzero in linked list. + INMOST_DATA_ENUM_TYPE IntervalBeg; ///< Begin of global interval of owned index interval + INMOST_DATA_ENUM_TYPE IntervalEnd; ///< End of global interval of owned index interval interval< INMOST_DATA_ENUM_TYPE, Row::entry > LinkedList; ///< Storage for linked list. + std::vector< INMOST_DATA_ENUM_TYPE > NonlocalPre; ///< List of global indices, that are to the left of owned index interval + std::vector< INMOST_DATA_ENUM_TYPE > NonlocalPost; ///< List of global indices, that are to the right of owned index interval public: + /// This function converts global index into local index. + /// @param pos Global index. + /// @return Local index. + INMOST_DATA_ENUM_TYPE MapIndex(INMOST_DATA_ENUM_TYPE pos) const; + /// This function converts local index into global index. + /// @param pos Local index. + /// @return Global index. + INMOST_DATA_ENUM_TYPE UnmapIndex(INMOST_DATA_ENUM_TYPE pos) const; + /// This function provides information about additional non-local indices. + /// \warning + /// All contents of linked list will be lost. + /// @param Pre Non-local indices that go before IntervalBegin. + /// @param Post Non-local indices that follow IntervalEnd. + void SetNonlocal(const std::vector & Pre, const std::vector & Post); /// Default constructor without size specified. RowMerger(); /// Constructor with size specified. @@ -559,30 +576,42 @@ namespace INMOST /// @param interval_end Last index in linked list. /// @param Sorted Result should be sorted or not. RowMerger(INMOST_DATA_ENUM_TYPE interval_begin, INMOST_DATA_ENUM_TYPE interval_end, bool Sorted = true); -#if defined(USE_SOLVER) - /// Constructor that gets sizes from the matrix. - /// @param A Matrix to get sizes from. - /// @param Sorted Result should be sorted. - RowMerger(Matrix & A, bool Sorted = true); -#endif //USE_SOLVER - /// Destructor. + /// Constructor with size and non-local mapping specified. + /// @param interval_begin First index in linked list. + /// @param interval_end Last index in linked list. + /// @param Pre Nonlocal indices before First index in linked list. + /// @param Post Nonlocal indices after Last index in linked list. + /// @param Sorted Result should be sorted or not. + RowMerger(INMOST_DATA_ENUM_TYPE interval_begin, INMOST_DATA_ENUM_TYPE interval_end, const std::vector & Pre, const std::vector & Post, bool Sorted = true); + /// Destructor. ~RowMerger(); /// Resize linked list for new interval. /// \warning /// All contents of linked list will be lost after resize. - /// This behavior may be changed in future. /// @param interval_begin First index in linked list. /// @param interval_end Last index in linked list. /// @param Sorted Result should be sorted or not. void Resize(INMOST_DATA_ENUM_TYPE interval_begin, INMOST_DATA_ENUM_TYPE interval_end, bool Sorted = true); + /// Resize linked list for new interval with non-local mapping. + /// \warning + /// All contents of linked list will be lost after resize. + /// @param interval_begin First index in linked list. + /// @param interval_end Last index in linked list. + /// @param Pre Nonlocal indices before First index in linked list. + /// @param Post Nonlocal indices after Last index in linked list. + /// @param Sorted Result should be sorted or not. + void Resize(INMOST_DATA_ENUM_TYPE interval_begin, INMOST_DATA_ENUM_TYPE interval_end, const std::vector & Pre, const std::vector & Post, bool Sorted = true); #if defined(USE_SOLVER) - /// Resize linked list for new matrix. + /// Constructor that gets sizes from the matrix, including non-local mapping. + /// @param A Matrix to get sizes from. + /// @param Sorted Result should be sorted. + RowMerger(const Matrix & A, bool Sorted = true); + /// Resize linked list for new matrix, including non-local mapping. /// \warning /// All contents of linked list will be lost after resize. - /// This behavior may be changed in future. /// @param A Matrix to get sizes from. /// @param Sorted Result should be sorted or not. - void Resize(Matrix & A, bool Sorted = true); + void Resize(const Matrix & A, bool Sorted = true); #endif //USE_SOLVER /// Clear linked list. void Clear(); @@ -593,14 +622,26 @@ namespace INMOST /// @param coef Coefficient to multiply row values. /// @param r A row to be added. /// @param PreSortRow Sort values of the row before adding. Will be activated only for sorted linked lists. - void PushRow(INMOST_DATA_REAL_TYPE coef, Row & r, bool PreSortRow = false); + void PushRow(INMOST_DATA_REAL_TYPE coef, Row & r, bool PreSortRow); /// Add a row with a coefficient into non-empty linked list. /// Use RowMerger::PushRow for empty linked list. /// @param coef Coefficient to multiply row values. /// @param r A row to be added. /// @param PreSortRow Sort values of the row before adding. Will be activated only for sorted linked lists. - void AddRow(INMOST_DATA_REAL_TYPE coef, Row & r, bool PreSortRow = false); - /// Multiply all entries of linked list by a coefficient. + void AddRow(INMOST_DATA_REAL_TYPE coef, Row & r, bool PreSortRow); + /// Add a row with a coefficient into empty linked list. + /// This routine should be a bit faster then RowMerger::AddRow + /// for empty linked list. It may result in an unexpected behavior + /// for non-empty linked list, asserts will fire in debug mode. + /// @param coef Coefficient to multiply row values. + /// @param r A row to be added. + void PushRow(INMOST_DATA_REAL_TYPE coef, const Row & r); + /// Add a row with a coefficient into non-empty linked list. + /// Use RowMerger::PushRow for empty linked list. + /// @param coef Coefficient to multiply row values. + /// @param r A row to be added. + void AddRow(INMOST_DATA_REAL_TYPE coef, const Row & r); + /// Multiply all entries of linked list by a coefficient. /// @param coef A coefficient for multiplication. void Multiply(INMOST_DATA_REAL_TYPE coef); /// Place entries from linked list into row. @@ -629,14 +670,16 @@ namespace INMOST /// @param a Row a. /// @param beta Multiplier for row b. /// @param b Row b. - void Merge(Row & c, INMOST_DATA_REAL_TYPE alpha, Row & a, INMOST_DATA_REAL_TYPE beta, Row & b) + void Merge(Row & c, INMOST_DATA_REAL_TYPE alpha, const Row & a, INMOST_DATA_REAL_TYPE beta, const Row & b) { PushRow(alpha,a); AddRow(beta,b); RetriveRow(c); Clear(); } + ///Retrive iterator for the first element. iterator Begin() {return iterator(&LinkedList);} + ///Retrive iterator for the position beyond the last element. iterator End() {iterator ret(&LinkedList); ret.pos = EOL; return ret;} }; #endif //defined(USE_SOLVER) || defined(USE_AUTODIFF) diff --git a/Source/IO/mesh_ecl_file.cpp b/Source/IO/mesh_ecl_file.cpp index 86015a4..9ee9b65 100644 --- a/Source/IO/mesh_ecl_file.cpp +++ b/Source/IO/mesh_ecl_file.cpp @@ -802,7 +802,7 @@ namespace INMOST entry.j = jj; entry.k1 = kk1; entry.k2 = kk2; - if( openshut == "OPEN" ) + if( std::string(openshut) == "OPEN" ) entry.open = true; else entry.open = false; diff --git a/Source/Solver/sparse.cpp b/Source/Solver/sparse.cpp index cb5fe12..9c807f8 100644 --- a/Source/Solver/sparse.cpp +++ b/Source/Solver/sparse.cpp @@ -58,10 +58,11 @@ namespace INMOST ////////class RowMerger - RowMerger::RowMerger() : Sorted(true), Nonzeros(0) {} + RowMerger::RowMerger() : Sorted(true), Nonzeros(0), IntervalBeg(0), IntervalEnd(0) {} INMOST_DATA_REAL_TYPE & RowMerger::operator[] (INMOST_DATA_ENUM_TYPE pos) { + pos = MapIndex(pos); if( LinkedList[pos+1].first != UNDEF ) return LinkedList[pos+1].second; else { @@ -94,46 +95,117 @@ namespace INMOST INMOST_DATA_REAL_TYPE RowMerger::operator[] (INMOST_DATA_ENUM_TYPE pos) const { + pos = MapIndex(pos); if( LinkedList[pos+1].first != UNDEF ) return LinkedList[pos+1].second; else throw -1; } RowMerger::RowMerger(INMOST_DATA_ENUM_TYPE interval_begin, INMOST_DATA_ENUM_TYPE interval_end, bool Sorted) - : Sorted(Sorted), Nonzeros(0), LinkedList(interval_begin,interval_end+1,Row::make_entry(UNDEF,0.0)) + : Sorted(Sorted), Nonzeros(0), IntervalBeg(interval_begin), IntervalEnd(interval_end), LinkedList(interval_begin,interval_end+1,Row::make_entry(UNDEF,0.0)) { LinkedList.begin()->first = EOL; } + RowMerger::RowMerger(INMOST_DATA_ENUM_TYPE interval_begin, INMOST_DATA_ENUM_TYPE interval_end, const std::vector & Pre, const std::vector & Post, bool Sorted) + : Sorted(Sorted), Nonzeros(0), IntervalBeg(interval_begin), IntervalEnd(interval_end), NonlocalPre(Pre), NonlocalPost(Post), LinkedList(interval_begin-Pre.size(),interval_end+Post.size()+1,Row::make_entry(UNDEF,0.0)) + { + assert(std::is_sorted(Pre.begin(),Pre.end())); + assert(std::is_sorted(Post.begin(),Post.end())); + LinkedList.begin()->first = EOL; + } + void RowMerger::Resize(INMOST_DATA_ENUM_TYPE interval_begin, INMOST_DATA_ENUM_TYPE interval_end, bool _Sorted) { - LinkedList.set_interval_beg(interval_begin); - LinkedList.set_interval_end(interval_end+1); + LinkedList.set_interval_beg(interval_begin-NonlocalPre.size()); + LinkedList.set_interval_end(interval_end+1+NonlocalPost.size()); + IntervalBeg = interval_begin; + IntervalEnd = interval_end; std::fill(LinkedList.begin(),LinkedList.end(),Row::make_entry(UNDEF,0.0)); LinkedList.begin()->first = EOL; Nonzeros = 0; Sorted = _Sorted; } + + void RowMerger::Resize(INMOST_DATA_ENUM_TYPE interval_begin, INMOST_DATA_ENUM_TYPE interval_end, const std::vector & Pre, const std::vector & Post, bool _Sorted) + { + NonlocalPre = Pre; + NonlocalPost = Post; + Resize(interval_begin,interval_end,_Sorted); + } #if defined(USE_SOLVER) - void RowMerger::Resize(Matrix & A, bool _Sorted) + void RowMerger::Resize(const Matrix & A, bool _Sorted) { - INMOST_DATA_ENUM_TYPE mbeg, mend; + INMOST_DATA_ENUM_TYPE mbeg, mend, k, l, ind; + //retrive interval of indices A.GetInterval(mbeg,mend); + //gather non-local mapping from matrix + std::set Pre, Post; + for(k = mbeg; k < mend; ++k) + { + for(l = 0; l < A[k].Size(); ++l) + { + ind = A[k].GetIndex(l); + if( ind < mbeg ) Pre.insert(ind); + else if( ind >= mend ) Post.insert(ind); + } + } + //sort to prepare for binary search + NonlocalPre.clear(); + NonlocalPre.insert(NonlocalPre.end(),Pre.begin(),Pre.end()); + NonlocalPost.clear(); + NonlocalPre.insert(NonlocalPost.end(),Post.begin(),Post.end()); Resize(mbeg,mend,_Sorted); } - RowMerger::RowMerger(Matrix & A, bool Sorted) : Sorted(Sorted), Nonzeros(0) + RowMerger::RowMerger(const Matrix & A, bool Sorted) : Sorted(Sorted), Nonzeros(0) { - INMOST_DATA_ENUM_TYPE mbeg, mend; - A.GetInterval(mbeg,mend); - LinkedList.set_interval_beg(mbeg); - LinkedList.set_interval_end(mend+1); - std::fill(LinkedList.begin(),LinkedList.end(),Row::make_entry(UNDEF,0.0)); - LinkedList.begin()->first = EOL; + Resize(A,Sorted); } #endif RowMerger::~RowMerger() {} + void RowMerger::SetNonlocal(const std::vector & Pre, const std::vector & Post) + { + assert(std::is_sorted(Pre.begin(),Pre.end())); + assert(std::is_sorted(Post.begin(),Post.end())); + NonlocalPre = Pre; + NonlocalPost = Post; + Resize(IntervalBeg,IntervalEnd,Sorted); + } + + + INMOST_DATA_ENUM_TYPE RowMerger::MapIndex(INMOST_DATA_ENUM_TYPE pos) const + { + if( pos < IntervalBeg ) + { + if(NonlocalPre.empty()) + std::cout << "pre " << NonlocalPre.size() << " post " << NonlocalPost.size() << " pos " << pos << " beg " << IntervalBeg << " end " << IntervalEnd << std::endl; + assert(!NonlocalPre.empty()); //there are indices provided + std::vector< INMOST_DATA_ENUM_TYPE >::const_iterator search = std::lower_bound(NonlocalPre.begin(),NonlocalPre.end(),pos); + assert(*search == pos); //is there such index? + return IntervalBeg - NonlocalPre.size() + static_cast(search-NonlocalPre.begin()); + } + else if( pos >= IntervalEnd ) + { + assert(!NonlocalPost.empty()); //there are indices provided + std::vector< INMOST_DATA_ENUM_TYPE >::const_iterator search = std::lower_bound(NonlocalPost.begin(),NonlocalPost.end(),pos); + assert(*search == pos); //is there such index? + return IntervalEnd + static_cast(search-NonlocalPost.begin()); + } + return pos; + } + + INMOST_DATA_ENUM_TYPE RowMerger::UnmapIndex(INMOST_DATA_ENUM_TYPE pos) const + { + if( pos < IntervalBeg ) + return NonlocalPre[pos+NonlocalPre.size()-IntervalBeg]; + else if( pos >= IntervalEnd ) + return NonlocalPost[pos-IntervalEnd]; + return pos; + } + + void RowMerger::Clear() { INMOST_DATA_ENUM_TYPE i = LinkedList.begin()->first, j; @@ -148,20 +220,27 @@ namespace INMOST Nonzeros = 0; } + void RowMerger::PushRow(INMOST_DATA_REAL_TYPE coef, Row & r, bool PreSortRow) { if( Sorted && PreSortRow ) std::sort(r.Begin(),r.End()); - //if( Nonzeros != 0 ) printf("nnz %d link %p proc %d\n",Nonzeros,this,omp_get_thread_num()); + PushRow(coef,r); + } + + void RowMerger::PushRow(INMOST_DATA_REAL_TYPE coef, const Row & r) + { assert(Nonzeros == 0); //Linked list should be empty assert(LinkedList.begin()->first == EOL); //again check that list is empty INMOST_DATA_ENUM_TYPE index = LinkedList.get_interval_beg(); - Row::iterator it = r.Begin(), jt; + Row::const_iterator it = r.Begin(), jt; while( it != r.End() ) { - LinkedList[index].first = it->first+1; - LinkedList[it->first+1].first = EOL; - LinkedList[it->first+1].second = it->second*coef; - index = it->first+1; + INMOST_DATA_ENUM_TYPE pos = it->first; + pos = MapIndex(pos); + LinkedList[index].first = pos+1; + LinkedList[pos+1].first = EOL; + LinkedList[pos+1].second = it->second*coef; + index = pos+1; ++Nonzeros; jt = it; ++it; @@ -172,32 +251,39 @@ namespace INMOST void RowMerger::AddRow(INMOST_DATA_REAL_TYPE coef, Row & r, bool PreSortRow) { if( Sorted && PreSortRow ) std::sort(r.Begin(),r.End()); + AddRow(coef,r); + } + + void RowMerger::AddRow(INMOST_DATA_REAL_TYPE coef, const Row & r) + { INMOST_DATA_ENUM_TYPE index = LinkedList.get_interval_beg(), next; - Row::iterator it = r.Begin(), jt; + Row::const_iterator it = r.Begin(), jt; while( it != r.End() ) { - if( LinkedList[it->first+1].first != UNDEF ) - LinkedList[it->first+1].second += coef*it->second; + INMOST_DATA_ENUM_TYPE pos = it->first; + pos = MapIndex(pos); + if( LinkedList[pos+1].first != UNDEF ) + LinkedList[pos+1].second += coef*it->second; else if( Sorted ) { next = index; - while(next < it->first+1) + while(next < pos+1) { index = next; next = LinkedList[index].first; } - assert(index < it->first+1); - assert(it->first+1 < next); - LinkedList[index].first = it->first+1; - LinkedList[it->first+1].first = next; - LinkedList[it->first+1].second = coef*it->second; + assert(index < pos+1); + assert(pos+1 < next); + LinkedList[index].first = pos+1; + LinkedList[pos+1].first = next; + LinkedList[pos+1].second = coef*it->second; ++Nonzeros; } else { - LinkedList[it->first+1].first = LinkedList[index].first; - LinkedList[it->first+1].second = coef*it->second; - LinkedList[index].first = it->first+1; + LinkedList[pos+1].first = LinkedList[index].first; + LinkedList[pos+1].second = coef*it->second; + LinkedList[index].first = pos+1; ++Nonzeros; } jt = it; @@ -206,6 +292,7 @@ namespace INMOST } } + void RowMerger::RetriveRow(Row & r) { r.Resize(Nonzeros); @@ -214,7 +301,7 @@ namespace INMOST { if( LinkedList[i].second ) { - r.GetIndex(k) = i-1; + r.GetIndex(k) = UnmapIndex(i-1); r.GetValue(k) = LinkedList[i].second; ++k; } -- 2.26.2