Assume every indicated segment lies within array bounds, and lengths are nonnegative. Use a temporary value of element type The first procedure even works for . For the second, disjointness ensures that each swap exchanges the two original entries at offset
T:PROCEDURE swap(i, j: INTEGER);
VAR x: T;
BEGIN
x := m[i]; m[i] := m[j]; m[j] := x
END swap;
PROCEDURE blockswap(i, j, n: INTEGER);
VAR t: INTEGER;
BEGIN
t := 0;
WHILE t < n DO
swap(i+t, j+t);
t := t + 1
END
END blockswap;t without disturbing any other pair. Its loop invariant says that offsets below t have been exchanged, offsets from t through retain their original values, and everything outside the two segments is unchanged. The loop variant proves algorithm termination. A block exchange uses swaps and auxiliary space. The stray wp printed after this source subpart is not an additional requirement.Let and be the lengths of the left and right blocks. Exchange the smaller block with the equally long block at the opposite end.If , split with . The first block swap sends to ; is now in its final suffix. Rotate the remaining prefix of length right by to send to . If , split with . The swap sends to ; the fixed prefix is , and rotating the remaining length- suffix right by restores . In both cases the exchanged segments are disjoint.
PROCEDURE rotate(i, n, k: INTEGER);
VAR a, b: INTEGER;
BEGIN
IF (k = 0) OR (k = n) THEN RETURN END;
a := n-k; b := k;
IF a = b THEN
blockswap(i, i+a, a)
ELSIF a < b THEN
blockswap(i, i+b, a);
rotate(i, b, b-a)
ELSE
blockswap(i, i+a, b);
rotate(i+b, a, b)
END
END rotate;The remaining length strictly decreases, so induction on proves algorithm termination and correctness. This implements the block-exchange rotation. It uses only simple explicit variables, but its recursive call stack can have depth when one block has length ; the iterative version removes that extra storage.
Keep both the exponent and its power. This Oberon procedure uses only the permitted arithmetic:The loop invariant is and . It holds initially because . A successful guard permits the next power, and the two assignments preserve the relation between and . At exit, , which is exactly part (a). Each iteration increases , and only finitely many powers of are at most , proving algorithm termination. The returned value is . Its running time is arithmetic operations; integer overflow is deferred to part (e).
PROCEDURE IntLog(b, n: INTEGER): INTEGER;
VAR i, p: INTEGER;
BEGIN
i := 0; p := 1;
WHILE p*b <= n DO
p := p*b; i := i+1
END;
RETURN i
END IntLog;Separate integer partitions according to whether they contain a part equal to . Removing one such part is a bijection to partitions of whose parts are at most . The alternatives are disjoint, givingUse , because the empty integer partition is counted once, and for , with for . This Oberon implementation avoids negative recursive arguments:The precondition is . Each recursive call reduces , proving algorithm termination. Every positive part of a partition of is at most , so the required one-argument count is . Counts require a sufficiently wide integer representation.
PROCEDURE P(i, n: INTEGER): INTEGER;
VAR answer: INTEGER;
BEGIN
IF n = 0 THEN answer := 1
ELSIF i = 0 THEN answer := 0
ELSIF i > n THEN answer := P(i-1, n)
ELSE answer := P(i-1, n) + P(i, n-i)
END;
RETURN answer
END P;
PROCEDURE Partitions(n: INTEGER): INTEGER;
BEGIN
RETURN P(n, n)
END Partitions;For a proposed loop invariant , initial condition , guard , body , and postcondition , establish these Hoare logic obligations:The first establishes before the loop; the second preserves it after each iteration; the third derives the desired result when the guard is false. Together they prove partial correctness. For total correctness, also prove algorithm termination, for example by a nonnegative integer loop variant that strictly decreases whenever the body executes. The loop invariant need not be the postcondition: it generally describes completed and unfinished work together.
Return the new root of the affected subtree, assigning that result back to the parent's child reference:Assume the input binary search tree satisfies part (b)'s representation invariant and, if the key already occurs, its count can be incremented. Structural induction proves the postcondition. For an empty subtree, the fresh entry represents one occurrence. Otherwise the word comparison identifies the only possible child subtree containing the word. The recursive call adds exactly one occurrence there, preserving the inherited strict bounds; in the equal case only the count changes. The number of entries in the current subtree strictly decreases in each recursive call, proving algorithm termination. The returned binary search tree represents the old multiset plus one occurrence of
def addToTree(word: String, t: Tree): Tree = {
require(word != null)
if (t == null) new Tree(word, 1)
else {
val c = word.compareTo(t.word)
if (c < 0) t.left = addToTree(word, t.left)
else if (c > 0) t.right = addToTree(word, t.right)
else {
require(t.count < Int.MaxValue)
t.count += 1
}
t
}
}
// Updating the whole structure uses: root = addToTree(word, root)word. With height , at most comparisons are made; without balancing this can be linear in the number of words. Actual comparison work also depends on the compared string data type lengths.An unfold expands a seed into either no node or a bucket and two new seeds:The seed is the remaining list. Expanding a nonempty seed partitions it exactly as
unfoldTree :: (s -> Maybe (s,[a],s)) -> s -> Tree a
unfoldTree split seed =
case split seed of
Nothing -> Empty
Just (left,equal,right) ->
Node (unfoldTree split left) equal (unfoldTree split right)
toTreeUnfold :: Ord a => [a] -> Tree a
toTreeUnfold = unfoldTree split
where
split [] = Nothing
split (pivot:xs) =
let (lo,eq,hi) = partition3 pivot xs
in Just (lo,pivot:eq,hi)toTree does, and both child seeds are shorter. Induction establishes toTreeUnfold = toTree and algorithm termination on finite inputs. The unfold separates the local decomposition rule from the recursive construction of the tree.A function returning an optional value supplies the representation:For a finite represented list, require a length such that every index returns
newtype List a = List (Int -> Maybe a)Just its element and every other index returns Nothing. Thus there are no holes: absence at a nonnegative index implies absence at every later index. The type alone does not enforce this invariant, or algorithm termination of a query. An infinite represented sequence may instead have a present value at every nonnegative index; operations requiring its length will not terminate.The implementations below return
Nothing at negative indices. Finite lengths, requested counts and intermediate indices are assumed to fit Int; explicit guards on index shifts also prevent arithmetic wraparound from exposing an invalid element.Merge by comparing the present heads and consuming just the chosen one. The implementation uses only the functional representation:The two recursive inputs are the disjoint initial and final portions of the represented sequence; they preserve every occurrence and have smaller lengths when . In
merge' :: Ord a => List a -> List a -> List a
merge' left right
| null' left = right
| null' right = left
| head' left <= head' right =
cons' (head' left) (merge' (tail' left) right)
| otherwise =
cons' (head' right) (merge' left (tail' right))
mergeSort' :: Ord a => List a -> List a
mergeSort' input =
let n = length' input
middle = n `div` 2
in if n <= 1 then input
else merge' (mergeSort' (take' middle input))
(mergeSort' (drop' middle input))merge', the smaller current head can precede every remaining value in both sorted inputs. Selecting the left head on equality preserves stability. Each merge step reduces the total number of remaining elements, and induction proves a sorted permutation of the original sequence.This establishes algorithm termination and correctness on total finite representations, not an unconditional implementation bound. Nested index functions can make queries costly, and repeatedly finding lengths or heads may revisit them. The familiar merge-sort bound assumes suitable constant-time access or consumption primitives; the source's function-only representation supplies no such cost guarantee.
Use the AAPCS32 convention:
r0 holds the input address, r1 the length, and r0 receives the result. The following Thumb instruction set assembly language uses only scratch processor registers, so no callee-saved registers need saving. Assume the array is valid, halfword-aligned, and has nonnegative length; for the mathematical sum assume it fits a signed 32-bit two's complement integer..syntax unified
.thumb
.thumb_func
sum16:
movs r2, #0 @ accumulated sum
cmp r1, #0
ble .Ldone @ empty array; also returns 0 if n < 0
.Lloop:
movs r3, #0 @ Thumb signed load needs a register offset
ldrsh r3, [r0, r3] @ signed halfword at a; offset read before r3 changes
adds r2, r2, r3 @ include this signed value
adds r0, r0, #2 @ advance by one short, not one byte
subs r1, r1, #1 @ one fewer element remains
bne .Lloop
.Ldone:
movs r0, r2 @ result register
bx lr @ leaf routine: lr was not overwrittenAfter iterations, the loop invariant is that
r2 contains the first elements' sum, r0 points to element , and r1 is . The sign extension in ldrsh supplies the correct signed addend. One iteration preserves this loop invariant, and the decreasing remaining length proves algorithm termination. For the seven-element calls, the sum lies between and , so it fits. For longer arrays that overflow, these instructions compute the sum modulo ; that is not a claim about defined signed C programming language overflow behavior. The operand forms are those of the paper's reference sheet.[ref]Thumb 16-bit instruction set quick reference by Arm. Load: signed halfword; Add/Subtract; Branch
A loop invariant is a property of the program state that holds whenever control reaches the loop's condition, including the final failed test. Its main purpose is proving partial correctness: establish it initially, prove one guarded iteration preserves it, then combine it with the negated condition to obtain the postcondition. A separate loop variant or another progress argument proves algorithm termination and hence total correctness.
Under exact integer arithmetic, the intended result is , using the ceiling function. A sufficient loop invariant isInitially , and the final disjunction is true. If the guard holds, the old value is below . Incrementing
i and doubling y therefore preserves and makes the new lower bound true. At exit . If , positivity of forces ; otherwise . Both cases establish the claimed postcondition. Doubling eventually reaches or exceeds any fixed positive , proving algorithm termination in this arithmetic model. This is the doubling-loop ceiling logarithm argument.The printed Scala implementation is not correct for its entire stated
Int domain. Its y is an Int. For , it reaches , wraps to on the next doubling, then becomes zero and stays zero. The guard remains true forever. The loop invariant above is valid for the printed code only when . Scala documents Int as a signed 32-bit value equivalent to Java's primitive int.[ref]A corrected implementation for every positive
Int input widens the doubling variable before any multiplication:def ceilingLog2(x: Int): Int = {
require(x > 0)
var i = 0
var y = 1L
while (y < x.toLong) {
i += 1
y *= 2L
}
i
}Now at most 31 iterations are needed and , safely within
Long. The same loop invariant proves total correctness without the overflow gap.Inside the supplied
PaymentsHash class, scan until a matching node or the end of the chain. A null return means the user has no record in this bucket.private def find(user: Int, head: PaymentsHash.Node): PaymentsHash.Node = {
var p = head
while (p != null && p.user != user) p = p.next
p
}The loop invariant says that every node already passed has a different user identity and
p heads the unexamined suffix. Short-circuit evaluation of && prevents dereferencing null. The representation invariant supplies an acyclic finite chain, so each unsuccessful step shortens that suffix and proves algorithm termination. The code changes no node fields. Postcondition 2026-10-18
The condition required of a final program state when a program finishes under its precondition. The summation loop verification requires returned value to satisfy and preserves the input . Relating the answer to the original input matters: allowing the program to replace by zero would change the intended contract.
A postcondition alone gives no promise of algorithm termination. It also need not specify a unique output: allows many answers, while the exact triangular number equation determines one integer answer for each permitted . Stronger postconditions express more obligations; proving only a weaker consequence cannot justify the stronger requirement.
Prefix maximum correctness example 2026-10-18
Given a nonempty finite sequence of integers, return its maximum value. Represent the sequence by an array whose contents remain unchanged during execution. The precondition requires an integer length and exact, terminating integer comparisons and variable assignments. The postcondition for returned value isThus the output belongs to the finite set of entries and is at least every entry. The contract permits ties and negative integers; it asks for a value, not the position of a particular occurrence. There is no maximum of an empty set of integers, so empty input is deliberately outside this precondition. A service accepting empty inputs needs a different formal specification, such as a separate absent result.
This Python implementation makes the variable assignments explicit. Its index arithmetic models exact integer arithmetic; fixed-width implementations additionally need enough index range for :For the original input , initialization gives and . Reading keeps ; reading changes it to ; reading and keeps . At successive condition checks, the pairs are . The returned maximum is .
def find_max(a):
n = len(a)
best = a[0]
k = 1
while k < n:
if a[k] > best:
best = a[k]
k = k + 1
return bestAt each condition check, use the loop invarianttogether with the fact that the input array is unchanged. The prefix is the first entries of the sequence, so its finite set of values is nonempty. The maximum need not be unique as an occurrence: the invariant concerns its value.
Initialization establishes : and , the maximum of a one-element set. For preservation, suppose and hold. Then is a valid array entry. Either the comparison replaces with the larger value , or it keeps a value at least . Before increasing , the resulting value isThe variable assignment restores the complete loop invariant. Between the two variable assignments, the old prefix formula may temporarily be false; the loop invariant is required at condition checks, not at every instruction.
At exit, , while gives . Hence , and gives exactly the required postcondition. This establishes partial correctness. The preservation argument is mathematical induction on the number of completed iterations, covering every permitted integer sequence, rather than just the pictured input.
For algorithm termination, the loop variant is a nonnegative integer whenever holds. If , then . Every body terminates under the precondition and changes to . Starting at , it reaches zero after exactly iterations. For , the initial value is already zero and the body is never entered. Together with partial correctness, this proves total correctness.
Starting at zero instead of fails on the original negative input : it returns zero, which is neither an entry nor the maximum . Keeping forever preserves the correct initial prefix claim but loops forever when ; a loop invariant alone does not ensure algorithm termination. Using
>= instead of > still returns the same maximum value when entries tie, although a version returning a position would need an explicit tie rule. Quickselect Created 2026-10-24 Updated 2026-10-31
A selection algorithm that uses array partitioning around a pivot and recursively searches only the side containing the desired order statistic. If there are entries before the placed pivot, an earlier rank is unchanged and a later rank is reduced by . Segment length strictly decreases, proving algorithm termination. The same reduction can update local bounds in a loop, using constant auxiliary space.
Balanced reductions give linear work by a geometric series, while repeatedly discarding only a constant number of entries can give quadratic work. For distinct entries and independent uniform random pivots, a pivot in the middle half has probability at least one half and reduces the retained length to at most three quarters, up to rounding. The expected number of attempts for each such reduction is bounded, so summing expected work over geometric size scales gives expected time. This random-pivot guarantee is not a worst-case guarantee for a deterministic median-of-three heuristic. With duplicates and a strict two-way partition, random pivot choice alone can also fail to shrink an all-equal segment; a three-way partition treats equal keys together.
Recursion (computer science) Created 2026-10-13 Updated 2026-10-18
Recursion solves a problem by invoking the same procedure on smaller instances, with base cases answered directly. For merge sort, an empty or one-entry array is already sorted, and every larger array is split into two shorter parts. The decreasing lengths justify termination; a self-call without a progress rule would not suffice.
A recursive procedure refers to itself, directly or through another procedure. A recursive call starts another invocation, with its own arguments and local variables; the caller can wait for its returned output. A base case supplies an answer without further recursive calls. To establish algorithm termination, also show that every recursive call progresses in a well-founded relation. “Smaller” means smaller in that chosen relation: argument values need not all decrease, and the arguments need not even be numbers.
Recursion is especially expressive when a data structure already decomposes into smaller structures. A binary tree has two child subtrees, so structural recursion can follow its shape. A short rule for each case can describe an entire traversal or computation. The distinction between a recursive procedure and the recursive process it generates helps connect that compact description to its resource use.[ref]
Structure and Interpretation of Computer Programs by Harold Abelson and others. Structure and Interpretation of Computer Programs, second edition, §1.2.1: procedure versus process; §1.2.2: closing discussion before Counting change, hierarchical data and straightforward definitions
Summation loop verification Created 2026-10-18 Updated 2026-10-31
Compute , the triangular number , for a nonnegative integer . The empty sum gives zero when . Use exact mathematical integers and keep the input unchanged; every comparison and variable assignment is a terminating operation. The original sequential procedure isThe order of the two variable assignments matters: the addition uses the newly incremented . For , the pairs at successive condition checks are . At the last check, is false, and the returned sum is .
require n is an integer and n >= 0
i = 0
s = 0
while i < n:
i = i + 1
s = s + i
return sThe precondition is with , under the exact-arithmetic model just stated. The postcondition for returned value is , with still equal to its starting value. At each condition check use the loop invariantand the loop variant . The equation says that the accumulator is the triangular number for precisely the processed prefix ; the inequalities connect that prefix to the loop's condition.
The summation loop restores its invariant before each condition check
. Original sequential control flow for the summation loop verification. The blue loop head carries the loop invariant; the orange body increments before adding it to . A false condition leads to the postcondition. The loop variant decreases on the return arrow. All values are mathematical integers and remains unchanged. Details.Initialization gives , so holds, including when . For preservation, let the old values satisfy and . Write and for the values after both variable assignments. Since are integers, gives , andThus the completed body restores . Between the two variable assignments, the equation can be false; the loop invariant is required at the loop head. This preservation step is mathematical induction on the number of completed iterations.
At exit, the false condition gives , while gives . Hence , so : this proves partial correctness. For algorithm termination, gives the nonnegative integer . When the condition is true, ; the terminating body gives . Therefore the loop cannot repeat forever. Together these arguments prove total correctness for every permitted input, including .
Two changes expose what the proof checks. Swapping the two body instructions computes , returning rather than for . Leaving unchanged can preserve an initial equation forever without producing any answer. The first change breaks the intended postcondition; the second breaks algorithm termination. A trace can reveal these failures, while the two proof obligations explain them.
The exact-arithmetic assumption has a concrete boundary. In a stipulated unsigned eight-bit model where each addition wraps modulo , fits in the input representation, but its intended triangular number is . The stored accumulator instead finishes at , so the mathematical postcondition fails. This is integer overflow, and the proof above does not apply to that altered model. Restricting the same implementation to prevents overflow in its stored loop values, since ; alternatively use a representation supporting the required exact integer arithmetic. The equations in the proof are mathematical statements, not instructions to evaluate products in eight bits.
Total correctness 2026-10-18
Total correctness requires partial correctness and algorithm termination for every program state allowed by the precondition. For a deterministic sequential program, every permitted start must reach a final program state satisfying the postcondition after finitely many steps. Thus an infinite execution can satisfy partial correctness, but refutes total correctness for a permitted start.
In the summation loop verification, the loop invariant establishes the answer conditional on exit, and the loop variant establishes exit. Both parts matter: replacing the body by a step that never returns would defeat total correctness even if every completed iteration preserved the loop invariant. Likewise, a terminating loop with the wrong sum fails partial correctness and therefore fails total correctness.
These obligations depend on the formal specification. A program that rejects every input might correctly implement a rejection service, but does not meet a contract promising a sum for every nonnegative integer. A proof of total correctness also gives no particular physical response-time guarantee: time complexity and the execution environment require their own assumptions.
While loop 2026-10-24
A while loop tests a Boolean condition before each iteration and repeats its body while that condition is true. A loop invariant is established initially and preserved by every iteration; a loop variant decreases in a well-founded order to prove algorithm termination. The invariant together with the false exit guard establishes the postcondition.


