diff --git a/src/index_single_mode.cpp b/src/index_single_mode.cpp index 6497ca9f..604fb6ed 100644 --- a/src/index_single_mode.cpp +++ b/src/index_single_mode.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -697,6 +698,43 @@ index_single_mode::IndexBuilder::computeNewKminmerRanges( return newKminmerRanges; } +namespace { +// Collapse a node's change record to one net entry per position, to handle a node that both edits a +// seed and deletes that seed's block. +void dedupeSyncmerChangeRecord( + std::vector>& rec, + const index_single_mode::SyncmerSet& finalMap) { + if (rec.size() < 2) return; + std::unordered_map> firstTouch; + firstTouch.reserve(rec.size()); + std::vector order; + order.reserve(rec.size()); + bool anyRepeat = false; + for (const auto& [pos, type, rsync] : rec) { + auto [it, inserted] = firstTouch.try_emplace(pos, type, rsync); + if (inserted) + order.push_back(pos); + else + anyRepeat = true; + } + if (!anyRepeat) return; + std::vector> out; + out.reserve(order.size()); + for (uint64_t pos : order) { + const auto& [firstType, firstRsync] = firstTouch[pos]; + const bool finalHas = finalMap.find(pos) != finalMap.end(); + if (firstType == panmapUtils::seedChangeType::ADD) { // parent lacked pos; keep only if it survives + if (finalHas) out.emplace_back(pos, panmapUtils::seedChangeType::ADD, seeding::rsyncmer_t()); + } else { // parent had pos; restore its pre-node value + out.emplace_back(pos, + finalHas ? panmapUtils::seedChangeType::SUB : panmapUtils::seedChangeType::DEL, + firstRsync); + } + } + rec = std::move(out); +} +} // namespace + void index_single_mode::IndexBuilder::buildIndexHelper(panmanUtils::Node* node, std::unordered_set& emptyNodes, panmapUtils::BlockSequences& blockSequences, @@ -929,6 +967,7 @@ void index_single_mode::IndexBuilder::buildIndexHelper(panmanUtils::Node* node, } } + dedupeSyncmerChangeRecord(refOnSyncmersChangeRecord, refOnSyncmersMap); std::vector> newKminmerRanges = computeNewKminmerRanges(refOnSyncmersChangeRecord, dfsIndex); @@ -1916,6 +1955,7 @@ void index_single_mode::IndexBuilder::processNode(panmanUtils::Node* node, std::vector addedSeedHashes; std::vector> substitutedSeedHashes; // + dedupeSyncmerChangeRecord(refOnSyncmersChangeRecord, state.refOnSyncmersMap); std::vector> newKminmerRanges = computeNewKminmerRanges(refOnSyncmersChangeRecord, state, dfsIndex); diff --git a/src/mgsr.cpp b/src/mgsr.cpp index 3c62e493..5707be28 100644 --- a/src/mgsr.cpp +++ b/src/mgsr.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -3259,6 +3260,42 @@ std::vector mgsr::mgsrIndexBuilder::computeNewSync return newSyncmerRanges; } +namespace { +// Collapse a node's change record to one net entry per position +void dedupeSyncmerChangeRecord( + std::vector>& rec, + const std::set& finalMap) { + if (rec.size() < 2) return; + std::unordered_map> firstTouch; + firstTouch.reserve(rec.size()); + std::vector order; + order.reserve(rec.size()); + bool anyRepeat = false; + for (const auto& [pos, type, rsync] : rec) { + auto [it, inserted] = firstTouch.try_emplace(pos, type, rsync); + if (inserted) + order.push_back(pos); + else + anyRepeat = true; + } + if (!anyRepeat) return; + std::vector> out; + out.reserve(order.size()); + for (uint64_t pos : order) { + const auto& [firstType, firstRsync] = firstTouch[pos]; + const bool finalHas = finalMap.find(pos) != finalMap.end(); + if (firstType == panmapUtils::seedChangeType::ADD) { + if (finalHas) out.emplace_back(pos, panmapUtils::seedChangeType::ADD, seeding::rsyncmer_t()); + } else { + out.emplace_back(pos, + finalHas ? panmapUtils::seedChangeType::SUB : panmapUtils::seedChangeType::DEL, + firstRsync); + } + } + rec = std::move(out); +} +} // namespace + std::vector::iterator, std::set::iterator>> mgsr::mgsrIndexBuilder::computeNewKminmerRanges( std::vector>& refOnSyncmersChangeRecord, @@ -3584,6 +3621,9 @@ void mgsr::mgsrIndexBuilder::buildIndexHelper(panmanUtils::Node* node, if (oldExists && !newExists) { if (blockOnSyncmers.find(blockId) != blockOnSyncmers.end()) { for (uint64_t pos : blockOnSyncmers[blockId]) { + // Skip: this position was already deleted earlier in this node (its + // blockOnSyncmers cleanup is deferred), so refOnSyncmers is nullopt here. + if (!refOnSyncmers[pos].has_value()) continue; refOnSyncmersChangeRecord.emplace_back( pos, panmapUtils::seedChangeType::DEL, refOnSyncmers[pos].value()); blockOnSyncmersChangeRecord.emplace_back(blockId, pos, panmapUtils::seedChangeType::DEL); @@ -3596,6 +3636,7 @@ void mgsr::mgsrIndexBuilder::buildIndexHelper(panmanUtils::Node* node, } } + dedupeSyncmerChangeRecord(refOnSyncmersChangeRecord, refOnSyncmersMap); std::vector::iterator, std::set::iterator>> newKminmerRanges = computeNewKminmerRanges(refOnSyncmersChangeRecord, dfsIndex); @@ -8264,4 +8305,4 @@ std::vector mgsr::getNearestNodes(mgsr::MgsrLiteNode* node, } return result; -} \ No newline at end of file +}