# Text diffing

This unit provides routines to compare to pieces of text and figure out what
lines we need to add and/or remove to the first one so that it becomes the same
as the second one.

It also provides routines to format this diff in the "Unified diff" standard.

This unit uses LF as a line separator.

## The algorithm

First of all, each text is separated into lines and each line has a checksum
computed. Therefore, what we compare here are two arrays of integers.

So, we begin on the first line on each side (let's call the original text the
"left" one and the new next the "right" one) and during the whole process, we
gradually "advance" on each side, not necessarily at the same speed.

The process consists of accumulating Hunk structures (see below). Our active
hunk beings at left/right indexes 0 and both counts to 0.

We begin by simply advancing as long as both sides are equal.

Then, as soon as we have a difference, we begin the Largest Common Sequence LCS
procedure, that is, we scn the right side for the largest block that matches our
current position on the left side and we also scan the left side for the largest
block that matches our current right side position.

The easiest case is when there is no match on either side. As long as there is
no match of the left side (that is, not a single line) in the right side, we
advance leftidx and increase delcnt. As long as there is no match on the right
side in the left side, we advance rightidx and increase addcnt.

If this brings us to a situations where we're equal again, close the hunk, we're
rolling.

Otherwise, this will bring us in a situation where we have two match blocks,
each with a skipcount and a matchcount figure. Now we need to choose the most
efficient side: deleting the left's skip count or adding the right's skip count.

After whichever of those operations, we can close the hunk and advance both
pointers by the matchcount of the chosen side.

To choose the best side, we assign a score to each, the score being
matchcount-skipcount. The highest (signed comparison) score wins.

And that's it, we repeat until we've reached the end of both sides.

When we reach one end before the other, then there's a last hunk with either
an addition or a deletion.

## API

### Constants

CONTEXTSZ  3. Number of lines around hunks we show off in diff printing.

### Structures

struct Hunk {
  uint leftidx rightidx delcnt addcnt ;
}

Structures yielded by diff[].

leftidx  The index in the left text where lines are removed or added.
rightidx The index in the right text where lines are added.
delcnt   Number of lines deleted on the left
addcnt   Number of lines added on the left, copied from the right

### Words

diff[] ( left u right u -- a u )
  Given two ranges of characters "left u" and "right u", transform them into
  arrays of CRCs and computes an array "a u" of Hunk structures (not pointers)
  which it yields.

  The CRCs and the Hunk structure need a temporary storage space, which is a
  static buffer managed by this routine using [mem/reuse]. The yielded result
  is only valid until the next call to "diff[]".

.diff[] ( left u right u -- )
  Do a diff check with "diff[]" and then print the changes in the form of a
  unified diff.

### Private words

max[]= ( a1 u1 a2 u2 -- n )
  Compare the two supplied ranges for equality and stop as soon as they're
  unequal, yielding the number of elements that match.

  Both arguments are aligned ranges of 32-bit elements.

lcs ( a1 u1 a2 u2 -- ?idx n-or-0 )
  In "a2 u2", find the largest sequence that matches the beginning of "a1 u1".

  Both arguments are aligned ranges of 32-bit elements.

  If the first element of "a1 u1" is not present anywhere in "a2 u2", yield u=0
  and idx is absent.

  Otherwise, yield the index, in "a2 u2" of the beginning of that sequence as
  well as the length of that matching sequence as "u".

diffcrcs[] ( left u right u -- ... n )
  Given two ranges of line checksums "left u" and "right u", compute the series
  of changes that must be applied to "left" in order to obtain "right.

  Those changes are yielded as a "... n" PS list of *quads* (not Hunk
  pointers) (leftidx, rightidx, delcnt, addcnt) with "addcnt" on PS top. They
  are yielded in reverse order of occurrence, that is, the last change on PS
  top.

lines>crcs ( ... n a u  -- ... n )
  With "a u" being a range characters, compute the crcs [num/crc] of each line
  (CR and/or LF are line separators), accumulating it into the "... n" PS list.

  This routine is designed to be ran on "readbuf" results, which means that
  lines can be cut in two. When input "n" is not 0, the top item of the PS list
  is the initial CRC. Otherwise, "crc32$" is called to get that initial value.

hunkctx ( hunk -- lo hi )
  Yield the context range a hunk covers in terms of low and high line indexes on
  the *left* side. CONTEXTSZ is both subtracted and added to enlarge the range.
  Deleted lines are also counted in that range so that this index range really
  represent the line coverage on the left.

grouphunks ( a u -- a u n )
  Given an array of Hunks as yielded by "diff[]", group the first hunks of the
  array as long as their context touch. Yield the number of hunks is that group.
