Algorithm Search⌘K

Proofs: Real-time queue

These are the human proofs of Real-time queue, at full length.

The real-time queue implements a persistent queue with O(1) worst-case operations full

This proof shows: correctness.

Values. A stream denotes a finite sequence: the empty stream denotes ⟨⟩\langle \rangle, and a stream whose suspension yields the cell (x,t)(x, t) denotes ⟨x⟩⋅val(t)\langle x \rangle \cdot \text{val}(t). Forcing a suspension does not change what it denotes, because the stored cell is the one its code yields. Write ∣t∣|t| for the length of val(t)\text{val}(t). For a list rr write rev(r)\text{rev}(r) for its items in reverse order. The items of a queue q=(f,r,s)q = (f, r, s) are items(q)=val(f)⋅rev(r)\text{items}(q) = \text{val}(f) \cdot \text{rev}(r).

Lemma 1 (rotate). If ∣r∣=∣f∣+1|r| = |f| + 1, then ROTATE(f,r,a)(f, r, a) denotes val(f)⋅rev(r)⋅val(a)\text{val}(f) \cdot \text{rev}(r) \cdot \text{val}(a).

Proof. By induction on ∣f∣|f|. If ff is empty, then r=[y]r = [y] and the result is ⟨y⟩⋅val(a)\langle y \rangle \cdot \text{val}(a), which is right. Otherwise ff yields (x,f′)(x, f') with ∣f′∣=∣f∣−1|f'| = |f| - 1, and r=y::r′r = y :: r' with ∣r′∣=∣f′∣+1|r'| = |f'| + 1. The result is ⟨x⟩\langle x \rangle followed by ROTATE(f′,r′,y::a)(f', r', y :: a), which by induction denotes val(f′)⋅rev(r′)⋅⟨y⟩⋅val(a)=val(f′)⋅rev(r)⋅val(a)\text{val}(f') \cdot \text{rev}(r') \cdot \langle y \rangle \cdot \text{val}(a) = \text{val}(f') \cdot \text{rev}(r) \cdot \text{val}(a). □\square

Invariant. Call q=(f,r,s)q = (f, r, s) good if (I1) ∣s∣=∣f∣−∣r∣|s| = |f| - |r| and (I2) ss is a suffix of ff (the same stream object reached after ∣f∣−∣s∣|f| - |s| tails) and every cell of ff before ss is forced.

Lemma 2 (exec). Let ff and ss satisfy (I2) for ff, and let ∣s∣=∣f∣−∣r∣+1|s| = |f| - |r| + 1. Then EXEC(f,r,s)(f, r, s) returns a good queue with the same items as val(f)⋅rev(r)\text{val}(f) \cdot \text{rev}(r).

Proof. If ss yields a cell (line L2), then the result is (f,r,s′)(f, r, s') with s′s' the tail of ss, so ∣s′∣=∣f∣−∣r∣|s'| = |f| - |r|, which is (I1). The head cell of ss is now forced, so every cell of ff before s′s' is forced, which is (I2). The items are unchanged. If ss is empty (line L3), then ∣f∣=∣r∣−1|f| = |r| - 1, and by (I2) all of ff is forced. By Lemma 1, $f' = $ ROTATE(f,r,empty)(f, r, \text{empty}) denotes val(f)⋅rev(r)\text{val}(f) \cdot \text{rev}(r), so the result (f′,nil,f′)(f', \text{nil}, f') has the same items, ∣s∣=∣f′∣=∣f′∣−0|s| = |f'| = |f'| - 0, and s=f′s = f' is a suffix of itself with no cells before it. □\square

Theorem 1 (correctness and persistence). Every queue made by the operations is good, and its items are those that the abstract queue gives to it. FRONT and POP return the first item. No operation changes the items of an existing version.

Proof. The empty queue is good. Let q=(f,r,s)q = (f, r, s) be good. PUSH calls EXEC with ff, x::rx :: r and ss; then ∣s∣=∣f∣−∣r∣=∣f∣−∣x::r∣+1|s| = |f| - |r| = |f| - |x :: r| + 1, and (I2) holds for ff and ss, so by Lemma 2 the result is good with items val(f)⋅rev(r)⋅⟨x⟩\text{val}(f) \cdot \text{rev}(r) \cdot \langle x \rangle, which is items(q)\text{items}(q) with xx at the back. For FRONT and POP, if qq is not empty then ∣f∣≥1|f| \ge 1, because ∣f∣≥∣r∣|f| \ge |r| by (I1) and ∣f∣+∣r∣≥1|f| + |r| \ge 1. So ff yields (x,f′)(x, f') and xx is the first item. POP calls EXEC with f′f', rr and ss. By (I1) and (I2), ss is a suffix of ff of length ∣f∣−∣r∣|f| - |r|. If ∣s∣<∣f∣|s| < |f|, then ss is also a suffix of f′f', the cells of f′f' before ss are cells of ff before ss and so are forced, and ∣s∣=∣f′∣−∣r∣+1|s| = |f'| - |r| + 1; Lemma 2 applies. If ∣s∣=∣f∣|s| = |f|, then s=fs = f and ∣r∣=0|r| = 0; EXEC finds that ss yields the cell (x,f′)(x, f') (line L2) and returns (f′,nil,f′)(f', \text{nil}, f'), which is good. The items are val(f′)⋅rev(r)\text{val}(f') \cdot \text{rev}(r), which is items(q)\text{items}(q) without xx. No procedure writes a cell, and forcing does not change values, so old versions keep their items. □\square

Theorem 2 (worst-case time). Each operation forces at most two suspensions that were not forced before, and each such force does O(1)O(1) work.

Proof. FRONT and POP force the head of ff. If ∣s∣<∣f∣|s| < |f|, that head is before ss and is forced by (I2). If s=fs = f, it may be unforced, and forcing it is one step. EXEC forces the head of ss, at most one step. A step of ROTATE(f,r,a)(f, r, a) forces the head of its argument ff and makes O(1)O(1) cells. For the outermost call made in line L3, the argument is fully forced by (I2), since ∣s∣=0|s| = 0 there. The inner calls receive tails of that same fully forced stream. So every step forces only cells that are already forced, which costs O(1)O(1) each, and builds O(1)O(1) new cells. A suspension shared by several versions is evaluated once, and later forces return the stored cell in O(1)O(1). Hence each operation costs O(1)O(1) in the worst case, on every version. ■\blacksquare

Back to Real-time queue