The algorithmic intention is exponentiation by squaring to compute for . With exact arithmetic, its loop invariant isInitially
result = 1, y = x, and i = n. For an even integer , squaring y and replacing i by preserves the product. For an odd integer , first multiply result by y; then . The nonnegative loop variant i decreases by halving whenever positive. At exit , so the loop invariant yields result = x^n. There are multiplication steps.Two qualifications are essential for the actual source. There is no requirement that
n be nonnegative: for every n <= 0 the printed function simply returns 1.0. Also, var y = x infers Int, so y *= y uses wrapping integer arithmetic, while the accumulator uses floating-point arithmetic. For example, anotherfunc(50000, 2L) returns -1794967296.0, rather than 2500000000.0. An exact-power loop invariant cannot prove a false claim about that execution.More precisely, for positive
n, after iterations the remaining exponent is , and the Int base is the signed 32-bit representative of modulo . The accumulator multiplies those signed representatives for the set bits of n, in increasing bit order, with a floating-point rounding after each multiplication. This describes the actual code even when the intended power law fails. In an exact implementation, use a nonnegative-exponent precondition and arbitrary-precision integers, for example:def powerExact(x: Int, n: Long): BigInt = {
require(x > 0 && n >= 0)
var result = BigInt(1)
var y = BigInt(x)
var i = n
while (i > 0) {
if (i % 2 == 1) result *= y
y *= y
i /= 2
}
result
}The displayed loop invariant applies exactly to this version. It performs logarithmically many arbitrary-precision multiplications; their bit cost grows with operand size, so that statement is not an bit-complexity bound. Merely making the accumulator a
Double does not widen the separate Int base variable.Save the next pointer before redirecting it, then advance the two working heads. No node is allocated and the name/number fields are untouched.
def reverseBook(): Unit = {
var prev: Node = null
var cur = list
while (cur != null) {
val following = cur.next
cur.next = prev
prev = cur
cur = following
}
list = prev
}Let be the original sequence of node identities, not just their possibly equal field values. Write for the sequence currently headed by
prev and for that headed by cur. A suitable loop invariant for in-place linked-list reversal iswith both working lists finite, acyclic, disjoint, together containing exactly the original nodes, and with every name and number unchanged. Here ++ means concatenation. Initially and . One iteration takes from the front of and replaces by . Since , the equation is preserved. Saving following before changing cur.next retains access to .The loop variant is the length of , decreasing by one. At exit , so ; assigning the head establishes reversal. The empty and singleton cases work without a special branch. The abstraction function of the phone book ignores order, so it still maps every original name to its original number. All original node identities are reused, with running time and auxiliary space.
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. Sign-bracketing binary search 2026-10-24
Suppose integer endpoints satisfy and . Repeatedly test an interior midpoint and replace the endpoint with its matching sign. The loop invariant is the ordered opposite-sign bracket. The positive integer width is a loop variant, and at termination . Thus an adjacent sign transition can be located in evaluations without assuming that is monotone. If extends continuously between the endpoints, the intermediate value theorem associates the sign bracket with a zero. This is different from finding the first true position of a globally monotone predicate.
Simultaneous exponentiation by squaring Created 2026-10-24 Updated 2026-11-05
Several powers with the same nonnegative integer exponent can share one exponentiation by squaring loop. Initialize , and . The loop invariants are .
At each step, multiply every by if is an odd integer, then replace every by and by . Indeed,so all invariants are preserved. The loop variant strictly decreases whenever it is positive. At , the accumulators are exactly the requested powers. For , no iteration is needed and every result is one.
For bases, the method uses multiplications and stored arithmetic values. These are arithmetic-operation counts; exact integer products can grow in bit length. For modular exponentiation, reducing each update modulo its intended integer modulus keeps the stored residues bounded. Sharing the exponent loop does not require mixing the bases' arithmetic types.
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.
Two-way array partition Created 2026-10-24 Updated 2026-10-31
Rearrange an array segment into a low segment containing values below a pivot and a high segment containing values at least , preserving the segment's multiset and leaving the rest of the array unchanged. The pivot need not itself occur, and either resulting segment may be empty.
An in-place scan maintains indices with and . A low value at is swapped into position and advances both indices; a high value advances only . The unexamined length is a loop variant. At exit , establishing the separation in time complexity and auxiliary space complexity. Equal values consistently belong to the high side.
Upper-bound search 2026-10-31
For a sorted array , find the unique boundary with . A binary search maintains , an excluded prefix , and an excluded suffix . At an interior midpoint , replace by when , and otherwise replace by . Sortedness preserves the loop invariant; the width is a decreasing loop variant. Termination at gives the boundary, including and . The time complexity is with constant-cost comparisons and indexed access. Unlike searching for , this formulation does not risk integer overflow at the largest representable key.
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.



