The finger tree operations keep the invariant and compute the right sequences full
This proof shows: correctness, termination.
We use the definitions of the page. For an element x, items(x) is the sequence of the items below it, and for a list of elements xs, items(xs) is the concatenation of their items. For a tree, seq(Empty)=⟨⟩, seq(Single(x))=items(x) and seq(Deep(pr,m,sf))=items(pr)⋅seq(m)⋅items(sf). The depth of a tree is 0 for Empty and Single, and 1+depth(m) for Deep(pr,m,sf). A valid tree of level k is a finger tree of level k that satisfies (F1) to (F3). Every constructor in the pseudocode computes its cached size from its parts, so (F3) holds for every new node and tree whose parts satisfy it; we do not repeat this below. No line changes a node or tree that already exists, so every earlier version keeps its sequence, and it suffices to prove the claims for the results.
Lemma 1 (CONS). If t is a valid tree of level k and a is an element of level k, then CONS(a,t) returns a valid tree of level k with sequence items(a)⋅seq(t).
Proof. By induction on the depth of t. Lines L1 and L2 return a Single or a Deep tree with digits of one element and an empty middle, which are valid, with the right sequence. Otherwise t=Deep(pr,m,sf). If pr=[b,c,d,e] (line L4), then Node3(c,d,e) is an element of level k+1 by (F1), and m is a valid tree of level k+1 of smaller depth, so by the hypothesis the recursive call returns a valid tree m′ of level k+1 with seq(m′)=items(c,d,e)⋅seq(m). The new prefix [a,b] has 2 elements, and the sequence is items(a,b)⋅items(c,d,e)⋅seq(m)⋅items(sf)=items(a)⋅seq(t). Otherwise pr has 1 to 3 elements and line L5 makes a prefix of 2 to 4 elements. □
By symmetry, SNOC(t,a) returns a valid tree with sequence seq(t)⋅items(a). Then, by induction on the length of xs, CONS-ALL(xs,t) has sequence items(xs)⋅seq(t) (line L6, the last element is added first), SNOC-ALL(t,xs) has sequence seq(t)⋅items(xs) (line L7), and TREE-OF(xs) has sequence items(xs) (line L8), all valid of the level of the elements.
Lemma 2 (VIEWL and DEEPL). Let k≥0.
- If t is a valid tree of level k, VIEWL(t) returns nil exactly when t=Empty, and otherwise a pair (a,t′) with a an element of level k, t′ a valid tree of level k, and items(a)⋅seq(t′)=seq(t).
- If pr is a list of 0 to 4 elements of level k, m a valid tree of level k+1 and sf a digit of level k, DEEPL(pr,m,sf) returns a valid tree of level k with sequence items(pr)⋅seq(m)⋅items(sf).
Proof. By induction on D, the depth of t in part 1 and 1+depth(m) in part 2. Part 1: lines L9 and L10 are direct. For t=Deep([a]++pr,m,sf) (line L11), pr has 0 to 3 elements, and part 2 applies to DEEPL(pr,m,sf) with the same value of D, since 1+depth(m)=depth(t). The sequences agree by the definition of seq. Part 2: if pr is non-empty (line L13), the Deep tree is valid. Otherwise part 1 applies to VIEWL(m), with a smaller value depth(m)<D. If it returns nil, m is Empty, and line L14 returns TREE-OF(sf), with sequence items(sf). Otherwise it returns a node n of level k+1 and a valid tree m′ of level k+1 with items(n)⋅seq(m′)=seq(m). The children of n are 2 or 3 elements of level k, a valid prefix, so the tree of line L15 is valid with sequence items(n)⋅seq(m′)⋅items(sf)=seq(m)⋅items(sf). □
By symmetry the same holds for VIEWR and DEEPR. In part 1 the induction measure decreases from D to a call of part 2 with the same D, which then calls part 1 with a smaller depth, so the recursion terminates.
Lemma 3 (NODES). If xs is a list of j elements of level k with 2≤j≤12, then NODES(xs) returns a list of ⌈j/3⌉ nodes of level k+1, between 1 and 4, whose children, in order, are xs.
Proof. By induction on j. Lines L16 to L18 cover j=2,3,4, with 1,1,2 nodes of 2 or 3 children, and ⌈j/3⌉ is 1,1,2. For j≥5 (line L19), the rest has j−3≥2 elements, so the hypothesis applies, and the result has 1+⌈(j−3)/3⌉=⌈j/3⌉ nodes. For j≤12 this is at most 4, and for j≥2 at least 1. The children are xs in order since the first node takes the first three elements. □
Lemma 4 (APP3). If t1 and t2 are valid trees of level k and ts is a list of 0 to 4 elements of level k, then APP3(t1,ts,t2) returns a valid tree of level k with sequence seq(t1)⋅items(ts)⋅seq(t2).
Proof. By induction on the depth of t1. Lines L20 to L23 follow from Lemma 1, its mirror image and the facts after it. In line L24 both trees are Deep. The list sf1++ts++pr2 has between 1+0+1=2 and 4+4+4=12 elements of level k, so by Lemma 3 NODES returns 1 to 4 nodes of level k+1 with the same items in order. The middles m1 and m2 are valid of level k+1 and m1 has smaller depth than t1, so by the hypothesis the recursive call returns a valid tree of level k+1 with sequence seq(m1)⋅items(sf1)⋅items(ts)⋅items(pr2)⋅seq(m2). With the digits pr1 and sf2 around it, the result has the sequence seq(t1)⋅items(ts)⋅seq(t2). □
With ts=[], APPEND is correct.
Lemma 5 (LOCATE). If 0≤i<size(xs), line L25 returns (k,j) such that position i of items(xs) is position j of items(xs[k]), with 0≤j<size(xs[k]).
Proof. The prefix sizes size(xs[1..k]) increase with k and reach size(xs)>i, so the least k with i<size(xs[1..k]) exists, and size(xs[1..k−1])≤i by minimality. The items of xs[k] occupy the positions from size(xs[1..k−1]) to size(xs[1..k])−1. □
Lemma 6 (SPLIT-TREE). If t is a valid non-empty tree of level k and 0≤i<size(t), then SPLIT-TREE(i,t) returns (l,x,r) where l and r are valid trees of level k, x is an element of level k, seq(l)⋅items(x)⋅seq(r)=seq(t), and size(l)≤i<size(l)+size(x).
Proof. By induction on the depth of t. Line L26: l and r are empty and 0≤i<size(x). Otherwise t=Deep(pr,m,sf).
Case i<size(pr) (line L27). By Lemma 5, cutting pr at LOCATE(i,pr) gives lists l and r of at most 3 elements and the element x that holds position i, with size(l)≤i<size(l)+size(x). TREE-OF(l) is valid with sequence items(l), and DEEPL(r,m,sf) is valid by Lemma 2 with sequence items(r)⋅seq(m)⋅items(sf). The three parts concatenate to seq(t).
Case size(pr)≤i<size(pr)+size(m) (line L28). Let i′=i−size(pr), so 0≤i′<size(m) and m is non-empty. By the hypothesis, SPLIT-TREE(i′,m) returns valid trees ml and mr of level k+1 and a node n of level k+1 with seq(ml)⋅items(n)⋅seq(mr)=seq(m) and size(ml)≤i′<size(ml)+size(n). So 0≤i′−size(ml)<size(n), and Lemma 5 applies to the 2 or 3 children of n, giving lists l and r of at most 2 elements and an element x of level k. DEEPR(pr,ml,l) and DEEPL(r,mr,sf) are valid by Lemma 2 and its mirror image, since pr and sf are digits. The sequences concatenate to items(pr)⋅seq(ml)⋅items(n)⋅seq(mr)⋅items(sf)=seq(t). The left part has size size(pr)+size(ml)+size(l), and by Lemma 5 position i′−size(ml) of items(n) lies in x after the items of l, which gives the position bounds.
Case i≥size(pr)+size(m) (line L29). Then 0≤i−size(pr)−size(m)<size(sf) since i<size(t). Note that line L29 computes i−size(m) after line L28 has already subtracted size(pr). Lemma 5 on sf and the mirror image of Lemma 2 for DEEPR(pr,m,l) give the claim as in the first case. □
Corollary (SPLIT-AT). For 0<i<n, SPLIT-TREE(i,t) on a valid tree of level 0 returns an item x with size(l)≤i<size(l)+1, so size(l)=i, and l and CONS(x,r) hold the first i items and the rest. For i≤0 and i≥n the answer is direct.
Lemma 7 (LOOKUP). If t is a valid tree of level 0 and 0≤i<size(t), LOOKUP(i,t) returns the item at position i of seq(t).
Proof. Let y be the wanted item. The loop of lines L30 and L31 keeps the invariant: t is a valid non-empty tree and y is at position i of seq(t), with 0≤i<size(t). When the loop condition holds, y lies in seq(m) at position i−size(pr) by the definition of seq, and m is non-empty, so the invariant holds for m. The loop stops, since the depth decreases. After it, t is a Single, or a Deep tree with i<size(pr) or i≥size(pr)+size(m). In the first case y is at position i of items(x). In the second case, line L33 makes y the item at position i of items(pr++sf): unchanged if it is in pr, and shifted by size(m) if it is in sf. The loop of line L34 keeps the invariant "y is at position i of items(xs), 0≤i<size(xs)" by Lemma 5. Each round goes one level down, so after at most the level of the elements of xs rounds, xs[k] is an item, and it is y. □
Theorem. Starting from Empty, every tree built by CONS, SNOC, VIEWL, VIEWR, APPEND and SPLIT-AT, applied to any earlier trees, is a valid tree of level 0, and each operation returns the sequences that the abstract type specifies. LOOKUP returns the item at the given position. All operations terminate.
Proof. Empty is valid. Earlier trees are never changed, so they stay valid. Lemmas 1, 2, 4, 6 and 7, the corollary, and their mirror images give validity and the sequences. Each recursion is on a tree of smaller depth or a shorter list, and each loop goes down one level or along a list of at most 12 elements per round, so every call terminates. ■