Proofs: Java system sort
These are the human proofs of Java system sort, at full length.
Both paths of the Java system sort sort the array, and the object path is stable full
This proof shows: correctness, termination.
Line L1 picks one of two paths. We prove each path correct. In both, each change of is an exchange, a move inside a range, or a merge of two neighbouring ranges, so always holds a permutation of the input.
The primitive path
Claim P. SORT-PRIMITIVE stops and leaves sorted, and it changes no item outside the range.
Proof. By induction on , and for equal lengths on . If , line L3 calls insertion sort, which is correct. If , line L4 calls heapsort, which is correct. Otherwise .
Line L5. The five sample indices with and lie strictly between and and are distinct. Sorting them by insertion and exchanging the second with and the fourth with gives .
Line L6. The loop keeps the invariant
(D) , , , .
It holds at the start with and , since the three parts are empty. Consider one step with and .
- If , exchanging and moves to the end of the left part, and the item at , which was in the middle part or was itself when , moves to . Then and both grow by 1, and (D) holds.
- If , the inner loop moves left past the items greater than , but not below . These items join the right part. If it stops with , then , and the exchange puts at , at the left end of the right part, and at . If it stops with , the exchange does nothing and at joins the right part. In both cases drops by 1. The new item at is at most , or . If it is less than , it goes to the left part as in the first case. Then grows by 1, and (D) holds.
- Otherwise , and grows by 1, which adds to the middle part.
Each step raises or lowers (in fact raises ), so the loop stops with .
Line L7. At the end, is the last index of the left part, or if it is empty, and is the first index of the right part, or . After and , exchanging with puts at and an item less than , or itself, at . Exchanging with puts at and an item greater than , or itself, at . Now , with .
Lines L8 and L9. The three ranges are strictly shorter than , because each misses at least the two pivots. By the induction hypothesis the calls stop and sort them. If , every item of the middle part is from to , so equal to , and the part is sorted without a call. Each item of a part is at most each item of the parts to its right, by the inequalities above, so is sorted.
Line L1 calls SORT-PRIMITIVE on the whole array, so by Claim P the array is sorted.
The object path
Invariant (R). At the start of each step of the loop of line L10, with the start of the next run, the stack holds runs with , and , each is sorted, and the items of each run are the items that the input held at the same positions, in an order that keeps equal items in their input order.
Initialization. The stack is empty and .
Line L10. The run at is ascending, or strictly descending and then reversed. A reversal of a strictly descending sequence gives an ascending sequence, and it has no equal items, so stability is kept. Binary insertion of each later item finds the first index whose item is greater than , so goes after all items equal to it, which came before it in the input, and the prefix stays sorted. So the new run is sorted and stable, and it starts at .
Lines L11 and L12. The push keeps (R). A merge of the runs and takes two neighbouring sorted runs and writes their items into the same positions in sorted order. It takes the item of the left run when the two items are equal, so it keeps equal items in the order of the runs, which is their input order by (R). The two runs become one run with the start of the first and the sum of the lengths, so (R) holds.
Termination. Each step of the loop of line L10 adds a run of length at least 1, so grows, and the loop stops when . Each merge removes one run from the stack, so the loops of line L11 and line L12 stop.
Correctness at the end. Line L12 merges until one run is left. By (R), it starts at 0, has length , is sorted, and keeps equal items in their input order. So the array is sorted, and the sort is stable. If the stack stays empty and there is nothing to sort.
The stack invariant of line L11 is not needed for correctness, only for the bound of runs on the stack and for the cost of .