Repository navigation
ops: say what changed between two versions of a document - #85
Merged
Merged
Conversation
ops.Compare and `pdfops compare`, BentoPDF's Compare PDFs. What a library can own of it: the PAIRING, and the categories that can be read out of the file -- text, pictures, annotations -- plus the two its pairing implies and its own list does not name, a page added and a page removed. Side by side and overlay are a viewer's job; "formatting" and "background" are a rendering comparison, which is a different instrument and a different cost. ⛔⛔ THE PAGES ARE PAIRED BY WHAT THEY SAY, NOT BY THEIR NUMBERS. A page inserted in the middle moves nothing after it: the pages that follow are still paired with the pages they came from, and the insertion is one PageAdded. Pairing by number would report that page as an edit of the next, that one as an edit of the one after, and so on to the end -- every page changed, which is both useless and false. ⛔ And a pair must share HALF ITS WORDS or it is one page removed and another added. Without the floor the alignment pairs the least unalike pages it can find, so a replaced chapter reads as a rewrite of the one before it. Inside a pair: Myers, E. W., "An O(ND) Difference Algorithm and Its Variations", Algorithmica 1 (1986) 251-266, over words, with the words that changed together reported together -- "quick brown fox" to "slow grey mole" is two changes and not six. ⛔⛔ THE WHOLE OF V IS KEPT EACH ROUND. The first version stored a row of 2d+1 and filled it as the round went, so a round that finished early left the diagonals after it as ZEROES -- and the walk back read those zeroes as real furthest points. It reported words removed and added that both pages had, and panicked on an index of -1 where the zeroes put it. The trace is the state, and half the state is not the state. The gate that found it is the equality oracle for any diff: the script must REBUILD BOTH SIDES, swept over 400 pseudo-random pairs with a fixed seed, because a hand-written case exercises one shape of edit and the walk back has a branch for each. ⛔ Two unreachable tails were REMOVED from the walk back rather than left untested: undoing every difference lands on the furthest point a path of no differences reached, which is the common prefix, so x == y by construction and the loops that emitted "whatever is left of the longer page" could never run. Bounds, because the input is two files and a file can say anything: the alignment is a cell per pair of pages and gives up past four million of them, pairing where the pages stand; the diff gives up past two thousand differences and reports both pages whole, rather than a diff of the half that was cheap. Both are vars so a test can lower them and watch it happen. ⛔ A picture is told apart by a HASH OF ITS SAMPLES -- two pictures of the same size are not the same picture, and holding both documents' bitmaps to find that out would turn a comparison into an exhausted machine. A reference that points at itself is a file anybody can write and no reader can follow; it stops the comparison with the page named, because a page reported as carrying no annotations because nobody could read them is the worst answer available. ⛔ `compare` exits 0 whether or not the documents differ, unlike `diff`: here an exit of 1 means the work could not be done, and a document that changed is the work being done. The word diff is generic and lives here anyway: `fleetfind diff` answers with rdiff and vcdiff, which are byte deltas for synchronising files, not Myers. It moves to a library the day a second caller wants it. ok github.com/go-pdfkit/ops 14.9s coverage: 100.0% of statements; covblocks: 0 blocks uncovered. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ops.Compareandpdfops compare: BentoPDF's Compare PDFs, the half alibrary can own.
Its categories are text, image, header/footer, annotation, formatting and
background. What is here is the pairing and the three that can be read out
of the file — text, pictures, annotations — plus the two its pairing implies
and its own list does not name: a page added and a page removed. Side by side
and overlay are a viewer's job and belong in the workbench; formatting and
background are a rendering comparison, which is a different instrument and a
different cost.
⛔ The pages are paired by what they say, not by their numbers
A page inserted in the middle moves nothing after it: the pages that follow
are still paired with the pages they came from, and the insertion is one
PageAdded. Pairing by number would report that page as an edit of the next,that one as an edit of the one after, and so on to the end — every page of the
document changed, which is both useless and false.
And a pair must share half its words, or it is one page removed and another
added. Without that floor the alignment pairs the least unalike pages it can
find, so a replaced chapter reads as a rewrite of the one before it.
The diff, and the bug the oracle caught
Myers, E. W., An O(ND) Difference Algorithm and Its Variations, Algorithmica
1 (1986) 251–266, over words, with the words that changed together reported
together: "quick brown fox" → "slow grey mole" is two changes, not six.
⛔⛔ The whole of V is kept each round. The first version stored a row of
2d+1 and filled it as the round went — so a round that finished early left the
diagonals after it as zeroes, and the walk back read those zeroes as real
furthest points. It reported words removed and added that both pages had, and
panicked on an index of −1 where the zeroes put it. The trace is the state, and
half the state is not the state.
The gate that found it is the equality oracle for any diff: the script must
rebuild both sides. Every other assertion about a diff is a matter of taste —
which of several shortest scripts it chose — but a script whose kept-and-removed
words are not the earlier page is simply wrong. Swept over 400 pseudo-random
pairs with a fixed seed, because a hand-written case exercises one shape of edit
and the walk back has a branch for each.
⛔ Two unreachable tails were removed from the walk back rather than left
untested: undoing every difference lands on the furthest point a path of no
differences reached, which is the common prefix, so
x == yby construction andthe loops that emitted "whatever is left of the longer page" could never run.
Bounds, because the input is two files
The alignment is a cell per pair of pages and gives up past four million of
them, pairing the pages where they stand; the diff gives up past two thousand
differences and reports both pages whole rather than a diff of the half that was
cheap. Both are vars, so a test lowers them and watches it happen.
⛔ A picture is told apart by a hash of its samples — two pictures of the
same size are not the same picture, and holding both documents' bitmaps to find
that out would turn a comparison into an exhausted machine.
⛔ A reference that points at itself is a file anybody can write and no
reader can follow. It stops the comparison with the page named, because a page
reported as carrying no annotations because nobody could read them is the
worst answer available.
⛔
compareexits 0 whether or not the documents differ, unlikediff: inthis command an exit of 1 means the work could not be done, and a document that
changed is the work being done.
The word diff is generic and lives here anyway:
fleetfind diffanswers withrdiff and vcdiff, which are byte deltas for synchronising files, not Myers. It
moves to a library of its own the day a second caller wants it; one caller is
not a shared concern.
ok github.com/go-pdfkit/ops 14.9s coverage: 100.0% of statements,covblocks: 0 blocks uncovered.🤖 Generated with Claude Code