Proofs: Block sort
These are the human proofs of Block sort, at full length.
Block sort returns the stable sorted order full
This proof shows: correctness, termination.
Tag each item with its input position. A sequence is stably sorted if its keys do not decrease and items with equal keys are in increasing order of their tags. For items write when belongs before in the stable order: the key of is less than that of , or the keys are equal and the tag of is smaller. This is a strict total order on the items, and "stably sorted" means increasing in .
We use these facts from other pages: the rotation by three reversals, and the rotation merge with its stability, from in-place merge sort; insertion sort is correct; LOWER-BOUND on a sorted range returns the first place whose item is not less than the key.
Termination. Every loop runs over a finite range with an index that moves one way, and the only recursion is the rotation merge, which ends. So BLOCK-SORT ends.
The outer sort. By induction on the levels, at the start of the level with width each run is stably sorted, and the tags in one run are smaller than those in the next. For this is line L1. A pair of runs that line L4 skips has its last left item not greater than its first right item, so its concatenation is already stably sorted. Any other pair is merged by BLOCK-MERGE, which by the theorem below makes it stably sorted. When there is one run. It remains to prove the theorem.
Theorem. Let and be stably sorted, with all tags of smaller than all tags of , and . Then BLOCK-MERGE leaves stably sorted and changes nothing outside it.
If , line L5 is the rotation merge, which is stable. Otherwise we follow the lines.
Line L7. The scan keeps the invariant: the found items form one block, in increasing order, each the first item of with its key, and the other items of seen so far keep their relative order, before the block. An item is added when its key is greater than the last found key; since is sorted, it is then the first copy of a new key. A rotation of the block forward past the gap keeps the order of the gap items. At the end, a rotation puts the block at the front. So holds the first copies of the smallest keys of , and is without them, still stably sorted.
Lemma 1 (putting the buffer back). Let hold distinct keys , each the first copy of its key in , and let hold the stably sorted order of the other items of . Then the rolling loop of redistribute in line L17 leaves the stably sorted order of all of them.
Proof. For each , the items of with the same key all have larger tags than : those in because is the first copy in , and those in because all tags of are larger. So in the stable order goes right before the first item of that is not less than it, which is what LOWER-BOUND finds. The loop handles first. It rotates the block past the items of that are less than , which keeps their order, and leaves right before the first item that is not less than it. Then is fixed, and the remaining block continues from there; the items it passes are greater than and are still in stable order. The keys increase, so each lands after , and by induction the result is the stable order.
Line L8. If , the rotation merge stably merges and into , and Lemma 1 finishes. From now on with .
Line L10. has items. It is cut into with items followed by A-blocks of items, and into B-blocks of items followed by the tail with fewer than items. There are blocks, so there are enough tags. Tag is the -th smallest buffer key, and it is set beside block , in the order A-blocks, then B-blocks. When , let be the tag of the first B-block; a block is an A-block if and only if its tag is less than , and this stays true as tags move with blocks.
Lemma 2 (the block order). After line L12, list the blocks, with among them, as from left to right, and let be the first item of . Then (a) the blocks from one run are in their original order, and (b) if , then when and come from different runs.
Proof. Line L11 is a selection sort on the key (first item, tag) compared as in the code: before if , or the keys are equal and the tag of is smaller. The tags are distinct, so this is a strict total order, and selection sort produces it; exchanging blocks of equal length with their tags keeps each block intact. For blocks of one run, the first items increase in with the original order, and on equal keys the tag order is the original order, so (a) holds. For blocks from different runs with equal first keys, the A-block has the smaller tag and comes first, and its first item also has the smaller input position; so the order of line L11 agrees with on first items, which gives (b).
For : every B-block comes before in , so for each B-block . Line L12 puts just before the longest suffix of blocks that are all A-blocks whose first key is greater than the key of . So each block after is an A-block with . Now let be an A-block before , and let be the block just before . If is an A-block, its key is at most that of , and by (a) so is the key of ; since is in and in , . If is a B-block, then is before , and by (b) . So (b) holds with placed, and (a) holds because is after every B-block.
Lemma 3 (the local merges). Before each step of lines L14 and L15, the array range of the blocks reads: the items made final so far, in stable order; then the swap space ; then , a stably sorted suffix of the items of one run seen so far; then in place. Every final item is every item of and of the blocks not yet handled.
Proof. At the start nothing is final and . is the beginning of , so its items are every other item of , but not necessarily of ; this is all the invariant needs, since nothing is final yet.
Same run (line L14). is the next block of 's run, by Lemma 2(a), so every item of has and is every later item of its own run. A later item of the other run lies in a block after , and by Lemma 2(b), ; the two are distinct items, so , and . So all of may become final. Moving before by exchanges keeps its order, and the new is a suffix of its run seen so far.
Different runs (line L15). The loop writes, at each step, the -smaller of the heads of and ; the test in the code is exactly this, as ties of keys go to the item from , which has the smaller tag. Each write exchanges an item of with the chosen item. Let . The writing place starts places before the head of and gains one place on it for each item taken from , so it reaches the unread items only after all of is read. So no unread item is overwritten, and the items of are only moved. The loop stops when one side is used up; two sides cannot run out at the same step, as each step takes one item. If was empty, nothing is written. Otherwise let be the head of the rest of the other side, and the last item of the used-up side, which was the last item written. Every written item is , and is every later item of its run. Every written item is , and is every later item of its own run, because the blocks of a run come in order. So the written items are everything not yet handled, and they may become final. If was used up, the code leaves as one block followed by the rest of . If was used up, the code moves the rest of to the right end with exchanges in decreasing order of position, which keeps its order and leaves as one block before it. In both cases the new is a suffix of one run seen so far.
Lines L16 and L17. After the last block, line L16 makes final by Lemma 3 with no blocks left, so the range of the blocks is the stable order of followed by . The rotation puts next to , so the buffer of distinct items is at the front, in some order, followed by the stable order of the other items. Insertion sort puts the buffer in increasing order, which is its stable order because the keys are distinct. Lemma 1 then gives the stable order of all of . Only was touched. This proves the theorem, and with the outer induction, BLOCK-SORT leaves stably sorted.