A generic sequence difference algorithm, based on E. W. Myers, "An O(ND) difference algorithm and its variations," Algorithmica, vol. 1, no. 1-4, pp. 251–266, 1986. To summarize, an edit script is computed by maintaining the furthest set of EditPoints which are reachable in a given number of edits D. This is used to compute the furthest set reachable with D+1 edits, and the process continues ind
| 375 | /// since most of the elements are shared between base and target and are represented |
| 376 | /// implicitly. |
| 377 | class QuadraticSpaceMyersDiff { |
| 378 | public: |
| 379 | QuadraticSpaceMyersDiff(const Array& base, const Array& target, MemoryPool* pool) |
| 380 | : base_(base), target_(target), pool_(pool) {} |
| 381 | |
| 382 | private: |
| 383 | // increment the position within base (the element pointed to was deleted) |
| 384 | // then extend maximally |
| 385 | EditPoint DeleteOne(EditPoint p) const { |
| 386 | if (p.base != base_end_) { |
| 387 | ++p.base; |
| 388 | } |
| 389 | return ExtendFrom(p); |
| 390 | } |
| 391 | |
| 392 | // increment the position within target (the element pointed to was inserted) |
| 393 | // then extend maximally |
| 394 | EditPoint InsertOne(EditPoint p) const { |
| 395 | if (p.target != target_end_) { |
| 396 | ++p.target; |
| 397 | } |
| 398 | return ExtendFrom(p); |
| 399 | } |
| 400 | |
| 401 | // increment the position within base and target (the elements skipped in this way were |
| 402 | // present in both sequences) |
| 403 | EditPoint ExtendFrom(EditPoint p) const { |
| 404 | const int64_t run_length_of_equals = |
| 405 | _comparator->RunLengthOfEqualsFrom(p.base, base_end_, p.target, target_end_); |
| 406 | p.base += run_length_of_equals; |
| 407 | p.target += run_length_of_equals; |
| 408 | return p; |
| 409 | } |
| 410 | |
| 411 | // beginning of a range for storing per-edit state in endpoint_base_ and insert_ |
| 412 | int64_t StorageOffset(int64_t edit_count) const { |
| 413 | return edit_count * (edit_count + 1) / 2; |
| 414 | } |
| 415 | |
| 416 | // given edit_count and index, augment endpoint_base_[index] with the corresponding |
| 417 | // position in target (which is only implicitly represented in edit_count, index) |
| 418 | EditPoint GetEditPoint(int64_t edit_count, int64_t index) const { |
| 419 | DCHECK_GE(index, StorageOffset(edit_count)); |
| 420 | DCHECK_LT(index, StorageOffset(edit_count + 1)); |
| 421 | auto insertions_minus_deletions = |
| 422 | 2 * (index - StorageOffset(edit_count)) - edit_count; |
| 423 | auto maximal_base = endpoint_base_[index]; |
| 424 | auto maximal_target = std::min( |
| 425 | target_begin_ + ((maximal_base - base_begin_) + insertions_minus_deletions), |
| 426 | target_end_); |
| 427 | return {maximal_base, maximal_target}; |
| 428 | } |
| 429 | |
| 430 | void Next() { |
| 431 | ++edit_count_; |
| 432 | // base_begin_ is used as a dummy value here since Iterator may not be default |
| 433 | // constructible. The newly allocated range is completely overwritten below. |
| 434 | endpoint_base_.resize(StorageOffset(edit_count_ + 1), base_begin_); |