Proofs: Swiss table
These are the human proofs of Swiss table, at full length.
The Swiss table keeps its invariant, and each find gives the right answer full
This proof shows: correctness, termination.
We use the notation of the page: groups of slots, a power of two, , and for a key the probe sequence . A slot is full when its control byte is in , and free when it is EMPTY or DELETED. For a full slot , . The invariant is:
(S1) A slot is full exactly when its control byte is in , and then the byte is . No key is in two full slots. and are the numbers of full and of DELETED slots.
(S2) For each full slot with key , let be the least index with . Then none of the groups holds an EMPTY slot.
(S3) .
Lemma 1 (coverage). For each key , the groups are all groups, each once. So the least index in (S2) exists and is less than .
Proof. This is Lemma 1 of the quadratic probing proof, with in place of : the triangular numbers for are distinct modulo .
Lemma 2 (the variable ). In SWISS-FIND and in lines L10 to L12 of SWISS-INSERT, the group in the iteration with counter is .
Proof. By induction on . For , . Line L6 or L12 adds to after it adds 1 to , and .
Lemma 3 (find). If (S1) to (S3) hold, SWISS-FIND returns the slot of if is in , and NIL otherwise, and it looks at fewer than groups. It changes nothing.
Proof. Line L4 returns a slot only if and its byte is , which is a full slot by (S1), so a returned slot holds . If is not in , line L4 never returns, and the find returns NIL at line L5 or does not end. By (S3) some slot is EMPTY, so some group has an EMPTY slot. By Lemmas 1 and 2, iteration reaches that group, and line L5 returns there. So the find ends within iterations.
Now let be in the full slot , and let be as in (S2). For , the group is not , and is in no other slot by (S1), so line L4 does not return. By (S2) the group has no EMPTY slot, so line L5 does not return. In iteration , the slot is in group and its byte is by (S1), so and line L4 returns . As , at most groups are read.
Lemma 4 (an EMPTY group stays EMPTY). Say that a group is open when it holds an EMPTY slot. Between two rebuilds, a group that is not open never becomes open.
Proof. Only two lines write EMPTY: line L22, in a rebuild, and line L18. Line L18 writes EMPTY into a slot of a group that is open at that time. So no line makes a group open that was not open before.
Lemma 5 (insert). If (S1) to (S3) hold, then after SWISS-INSERT they hold again, , and the other pairs do not change.
Proof. By Lemma 3, line L7 finds the slot of if it is present, and line L8 then changes only its value, which keeps (S1) to (S3).
Otherwise is not in . If line L9 calls REBUILD, assume for now that REBUILD gives a table with the same pairs in which (S1) to (S3) hold and (Lemma 7). So at line L10, , and as some slot is free. By Lemmas 1 and 2, the loop of lines L11 and L12 ends at the first group with a free slot, after steps. Line L13 takes a free slot of that group, line L14 updates if was DELETED, and line L15 makes full with byte and adds 1 to .
(S1) holds: was not present, so it is now in one slot, and the counts are right. (S3) holds: grows by 1 if was EMPTY and stays the same if was DELETED, and before the insert . (S2) for : the least index with is , because the groups are distinct. The groups had no free slot, so no EMPTY slot, and line L15 does not change them. (S2) for the other keys: they do not move, and the only change is that a free slot becomes full, which does not add an EMPTY slot to any group.
Lemma 6 (erase). If (S1) to (S3) hold, then after SWISS-ERASE they hold again, is not in , and the other pairs do not change. The result is true exactly when was in .
Proof. By Lemma 3, line L16 finds the slot of , or NIL, and then line L17 returns false and nothing changes. Otherwise lines L18 to L20 make free and lower by 1, so is no longer in , and (S1) holds with the new counts. does not grow, so (S3) holds.
For (S2), take another key at a full slot , with its index . If line L19 runs, no slot becomes EMPTY, so (S2) for still holds. If line L18 runs, the group was open before the erase. If were one of , the old (S2) for would say that is not open, which is false. So is not among these groups, and they do not change. (S2) for holds.
Lemma 7 (rebuild). REBUILD makes a table with the same pairs in which (S1) to (S3) hold, , and , where is the new number of slots. It ends.
Proof. Line L21 doubles until . This ends, because grows without a bound. The new table has all slots EMPTY, so (S1) to (S3) hold with no pairs. Line L22 puts the old pairs in one at a time by lines L10 to L15. They are distinct keys, so each one is not yet present, and before each put , so the argument of Lemma 5 applies to each put and keeps (S1) to (S3). At the end the table holds the old pairs, , and .
Theorem. Start from a table of EMPTY slots and apply SWISS-INSERT, SWISS-ERASE and SWISS-FIND in any order. Then (S1) to (S3) hold after each operation, the pairs in the table are the map given by the operations, and each find returns the slot of its key or NIL as it should. Each operation ends.
Proof. The EMPTY table has and no full slots, so (S1) to (S3) hold, and is empty. Lemmas 5 and 6 keep the invariant and change the pairs as the dictionary asks, and Lemma 3 gives the answer of each find. By induction on the number of operations, the claim holds after each one. Each find reads at most groups, each insert or erase runs one find, at most steps of lines L11 and L12, and at most one rebuild, and the rebuild ends by Lemma 7.
Remark. Lemma 4 is not used in the proofs above, but it explains line L18: an open group was open at each moment since the last rebuild, so no find for a present key has ever moved past it, and a slot there can be made EMPTY. Abseil's rule (count the slots that are not EMPTY next to the erased slot, and write EMPTY if the count is less than ) is the same idea for unaligned windows of slots: if no window of slots without an EMPTY slot covers the erased slot, no probe has moved past it.