Algorithm Search⌘K

Proofs: Piece table

These are the human proofs of Piece table, at full length.

The piece table operations keep the invariant and implement the list operations full

This proof shows: correctness.

We use the invariant (P1) to (P3) of the page. Write Lk=ℓ(p0)+⋯+ℓ(pk−1)L_k = \ell(p_0) + \cdots + \ell(p_{k-1}) for the prefix sums of the piece lengths, so L0=0L_0 = 0 and LP=nL_P = n by (P2). By (P2), position jj of the text, for Lk≤j<Lk+1L_k \le j < L_{k+1}, is character j−Lkj - L_k of str(pk)str(p_k).

Lemma 1 (FIND). For 0≤i<n0 \le i < n, FIND returns the unique (k,off)(k, off) with Lk≤i<Lk+1L_k \le i < L_{k+1} and off=i−Lkoff = i - L_k. For i=ni = n it returns (P,0)(P, 0).

Proof. Before the round for kk, off=i−Lkoff = i - L_k: this holds for k=0k = 0, and line L4 subtracts ℓ(pk)\ell(p_k), which makes it i−Lk+1i - L_{k+1}. The round returns at the first kk with i−Lk<ℓ(pk)i - L_k < \ell(p_k), that is i<Lk+1i < L_{k+1}, and i≥Lki \ge L_k holds since the earlier rounds did not return. By (P1) the LkL_k are strictly increasing, so this kk is unique. If no round returns, then i≥LP=ni \ge L_P = n, so i=ni = n. □\square

CHAR-AT. By Lemma 1 and the remark above, line L22 returns position ii of the text, and s+offs + off is in range by (P1).

INSERT. Let x≠εx \ne \varepsilon and a=∣T.add∣a = \lvert T.add \rvert before line L7. After line L7, T.add[a..a+∣x∣−1]=xT.add[a .. a + \lvert x \rvert - 1] = x, and by (P3) the strings of the old pieces do not change. Let tt be the text before the call. We show that the new piece list spells t[0..i−1]⋅x⋅t[i..]t[0 .. i-1] \cdot x \cdot t[i ..].

  • Line L9. Here off=0off = 0, so i=Lki = L_k: position ii is the boundary after pk−1p_{k-1}. The piece pk−1=(add,s,ℓ)p_{k-1} = (add, s, \ell) with s+ℓ=as + \ell = a names T.add[s..a−1]T.add[s .. a-1], and the new piece (add,s,ℓ+∣x∣)(add, s, \ell + \lvert x \rvert) names T.add[s..a−1]⋅xT.add[s .. a-1] \cdot x. So the text becomes str(p0)⋯str(pk−1)⋅x⋅str(pk)⋯str(p_0) \cdots str(p_{k-1}) \cdot x \cdot str(p_k) \cdots, as required. The new piece is in range since s+ℓ+∣x∣=∣T.add∣s + \ell + \lvert x \rvert = \lvert T.add \rvert.
  • Line L10. Again i=Lki = L_k (including k=Pk = P, i=ni = n), and the new piece (add,a,∣x∣)(add, a, \lvert x \rvert) names xx, put between pk−1p_{k-1} and pkp_k.
  • Line L11. Here 0<off<ℓ(pk)0 < off < \ell(p_k), and str(pk)=bufb[s..s+off−1]⋅bufb[s+off..s+ℓ−1]str(p_k) = buf_b[s .. s + off - 1] \cdot buf_b[s + off .. s + \ell - 1]. The three new pieces spell str(pk)[0..off−1]⋅x⋅str(pk)[off..]str(p_k)[0 .. off-1] \cdot x \cdot str(p_k)[off ..], and position ii is offset offoff in pkp_k by Lemma 1. All three have positive length because 0<off<ℓ0 < off < \ell, and they are in range.

In each case the new pieces have length at least 1 and lie in their buffers, so (P1) holds; the text is as required and T.nT.n grows by ∣x∣\lvert x \rvert, so (P2) holds; only appends were made to T.addT.add, so (P3) holds.

DELETE. Let 0≤i≤i+d≤n0 \le i \le i + d \le n and let tt be the text before the call. Call the characters at positions ii to i+d−1i + d - 1 the doomed characters. We prove that the loop keeps this claim: the current piece list spells tt with the doomed characters that were already removed taken out; the pieces before index kk hold no doomed character; the dd doomed characters that remain are the first dd characters starting at offset offoff of pkp_k and the pieces after it; and (P1) holds.

At the start, the claim holds by Lemma 1, with nothing removed yet. In a round, t′=min⁡(d,ℓ−off)t' = \min(d, \ell - off) is the number of remaining doomed characters inside pkp_k, at offsets offoff to off+t′−1off + t' - 1.

  • Line L16 (off=0off = 0, t′=ℓt' = \ell): the whole piece is doomed and is removed; the next piece is now at index kk, and its offset 0 is the next doomed character.
  • Line L17 (off=0off = 0, t′<ℓt' < \ell): the head of length t′t' is removed and the rest is kept; then d−t′=0d - t' = 0, so the loop ends after this round.
  • Line L18 (off>0off > 0, off+t′=ℓoff + t' = \ell): the tail is removed; the next doomed character, if any, is at offset 0 of pk+1p_{k+1}, which becomes the current piece.
  • Line L19 (off>0off > 0, off+t′<ℓoff + t' < \ell): the middle is removed, and the head and the tail are kept as two pieces of positive length; again d−t′=0d - t' = 0.

In each case the pieces made have positive length and lie in their buffers, so (P1) holds, and line L20 updates dd and sets off=0off = 0 in agreement with the claim. Each round removes t′≥1t' \ge 1 characters, because off<ℓoff < \ell (by Lemma 1 for the first round, and off=0<ℓoff = 0 < \ell later), so the loop ends, and at the end all dd doomed characters are removed. The buffers are untouched, so (P3) holds, and line L12 sets T.nT.n to the new length, so (P2) holds. ■\blacksquare

Back to Piece table