Algorithm Search⌘K

Proofs: Gap buffer

These are the human proofs of Gap buffer, at full length.

The gap buffer operations keep the sequence and move the cursor correctly full

This proof shows: correctness.

Invariant (I). cap≥1cap \ge 1, 0≤g≤e≤cap0 \le g \le e \le cap, and the sequence held is S=⟨A[0],…,A[g−1],A[e],…,A[cap−1]⟩S = \langle A[0], \ldots, A[g-1], A[e], \ldots, A[cap-1] \rangle, with the cursor at position gg. Write n=cap−(e−g)n = cap - (e - g) for its length, and w=e−gw = e - g for the width of the gap.

Reading by position. Under (I), the first gg items of SS are A[0..g−1]A[0..g-1], and item i≥gi \ge g is A[e+(i−g)]=A[i+w]A[e + (i - g)] = A[i + w]. So line L7 returns item ii of SS for each 0≤i<n0 \le i < n.

MOVE-TO(B,p)(B, p), 0≤p≤n0 \le p \le n. We show that each pass of either loop keeps (I), keeps SS and ww, and changes gg by one toward pp.

A pass of line L1, with g>p≥0g > p \ge 0. Before it, S=⟨A[0],…,A[g−1],A[e],…⟩S = \langle A[0], \ldots, A[g-1], A[e], \ldots \rangle. The pass sets e′=e−1e' = e - 1, g′=g−1g' = g - 1, then writes A[e′]←A[g′]A[e'] \leftarrow A[g'], the old A[g−1]A[g-1]. Since g≤eg \le e, we have g′≤e′g' \le e' and 0≤g′0 \le g'. Cell e′e' was a gap cell if w≥1w \ge 1, and is cell g′g' itself if w=0w = 0; in both cases no item other than A[g−1]A[g-1] is overwritten. The new sequence is ⟨A[0],…,A[g′−1],A[e′],A[e],…⟩\langle A[0], \ldots, A[g'-1], A[e'], A[e], \ldots \rangle with $A[e'] = $ the old A[g−1]A[g-1], which is the old SS. The width is e′−g′=we' - g' = w.

A pass of line L2, with g<p≤ng < p \le n. Then g<ng < n, so e<cape < cap and A[e]A[e] is the first item after the gap. The pass writes A[g]←A[e]A[g] \leftarrow A[e] and sets g′=g+1g' = g + 1, e′=e+1≤cape' = e + 1 \le cap. Cell gg was a gap cell, or is cell ee itself if w=0w = 0. The new sequence is ⟨A[0],…,A[g−1],A[g],A[e′],…⟩\langle A[0], \ldots, A[g-1], A[g], A[e'], \ldots \rangle with A[g]A[g] the old A[e]A[e], which is the old SS. The width is unchanged.

Line L1 runs exactly g−pg - p passes when g>pg > p, and then line L2 runs none; line L2 runs exactly p−gp - g passes when g<pg < p. Afterwards g=pg = p. So MOVE-TO keeps SS, puts the cursor at pp, and moves ∣p−g∣\lvert p - g \rvert items.

GROW(B)(B), called with g=eg = e. Line L8 makes CC of size 2⋅cap2 \cdot cap, copies A[0..g−1]A[0..g-1] to C[0..g−1]C[0..g-1], and copies A[e..cap−1]A[e..cap-1], which has cap−ecap - e items, to C[2cap−(cap−e)..2cap−1]=C[e+cap..2cap−1]C[2cap - (cap - e) .. 2cap - 1] = C[e + cap .. 2cap - 1]. It sets e′=e+cape' = e + cap and cap′=2capcap' = 2cap. Then g≤e′g \le e', e′≤cap′e' \le cap', and the sequence ⟨C[0],…,C[g−1],C[e′],…,C[cap′−1]⟩\langle C[0], \ldots, C[g-1], C[e'], \ldots, C[cap'-1] \rangle is the old SS. The new width is cap≥1cap \ge 1.

INSERT(B,x)(B, x). After line L3, (I) holds with the same SS and g<eg < e: either g<eg < e already, or GROW made the width cap≥1cap \ge 1. Line L4 writes xx into A[g]A[g], a gap cell, and sets g′=g+1≤eg' = g + 1 \le e. The new sequence is ⟨A[0],…,A[g−1],x,A[e],…⟩\langle A[0], \ldots, A[g-1], x, A[e], \ldots \rangle: xx is inserted at the old cursor position, and the cursor is just after it.

DELETE-BEFORE(B)(B), g>0g > 0. Line L5 sets g′=g−1g' = g - 1 and returns A[g′]A[g'], the item just before the cursor. Now 0≤g′<e0 \le g' < e, and the sequence is SS without that item. No cell is written.

DELETE-AFTER(B)(B), e<cape < cap. Line L6 sets e′=e+1≤cape' = e + 1 \le cap and returns A[e]A[e], the item just after the cursor. The sequence is SS without that item, and the cursor stays at gg.

Amortized cost of INSERT. Let Φ=2n−cap\Phi = 2n - cap when it is positive, and 0 otherwise. An insert without a grow costs 1 and raises Φ\Phi by at most 2. A grow happens when n=capn = cap, so Φ=cap\Phi = cap before it; it copies capcap items, and afterwards Φ=2cap−2cap=0\Phi = 2cap - 2cap = 0, so the copies are paid by the drop in Φ\Phi. Deletes and moves do not raise Φ\Phi. So each insert costs at most 3 amortized, and mm operations with a total cursor distance DD cost O(m+D)O(m + D), from an initial state with Φ=0\Phi = 0. ■\blacksquare

Back to Gap buffer