Array bounds 2026-10-31
The permitted array indices for a length- array are under zero-based indexing. A one-past-the-end index can describe an empty remaining segment but cannot be dereferenced. Proving bounds safety requires showing that every executed read and write uses a permitted index, including evaluations in a loop guard. A known target inside the array can sometimes supply a stronger loop invariant than an explicit bounds test.
Array index 2026-10-18
An integer identifying a position in an array. With zero-based indexing and capacity , valid array indices are . An array index identifies the slot rather than its stored value.
Binary array sum by recursion 2026-10-18
Keep an array unchanged, and sum entries in the half-open integer interval of array indices . The precondition requires integers and exact, terminating integer addition. An empty range returns the empty sum zero; a one-entry range returns that entry. Every longer range splits at its middle array index:For the original array , call . The left half returns , the right half returns , and the original invocation returns . The right half's left endpoint increases from zero to two: what decreases is range length , not every argument. Negative entries affect the answer but do not affect algorithm termination.
S(A, l, r):
require integer indices with 0 <= l <= r <= length(A)
size = r - l
if size == 0:
return 0
if size == 1:
return A[l]
middle = l + floor(size / 2)
left_sum = S(A, l, middle)
right_sum = S(A, middle, r)
return left_sum + right_sumSeven calls compute an array sum with only three frames active at once
. Original binary tree of invocations for , with integer intervals labeling ranges and upward arrows labeling returned sums. Small numbers give the order in which invocations begin when the left child runs first. The highlighted graph path contains the three active stack frames while the entry is read; the result from the completed left sibling is retained by its parent. Details.For range length , the children have lengths and , both positive and strictly less than . The floor function chooses the split; the ceiling function gives the other length. Algorithm termination follows by this natural number measure. For algorithm correctness, apply mathematical induction on : the two base cases return the specified sum; for a larger range, the two disjoint child ranges cover every original array index exactly once, so addition of their correct sums gives the required sum.
For , the invocation tree is a full binary tree with leaves, one per array entry. If is its number of nonleaf vertices, counting edges gives , so . Thus the total invocation count is , and there are combining additions. Here gives seven calls and three combining additions. For , there is one call and no combining addition; for , there is also one call and no combining addition, but no entry leaf, so the formula does not apply.
The call stack holds the active branch, rather than the entire invocation tree. Execute the left child completely before the right child, retaining the left result in the parent stack frame. The maximum number of active stack frames is for , and one for . Balanced splitting therefore needs frames for , compared with the frames of the factorial recursive process. Assuming constant-cost array access, index operations and addition, its time complexity is and its auxiliary space complexity is machine words for , excluding the stored input. Pass the same array with different endpoints, rather than copying slices. These bounds require all stored indices and partial sums to fit in a constant number of machine words; exact unbounded integer addition and storage require a separate bit cost analysis.
Circular buffer Created 2026-10-13 Updated 2026-10-18
A circular buffer stores a changing sequence in a fixed-capacity array, reusing slots by wrapping array indices through modular arithmetic. A front array index and an occupied count can represent a FIFO queue without shifting entries after removal. The physical array stays in place; the position at which its logical sequence begins moves. This makes a circular buffer useful when storage must remain bounded while successive items arrive and leave.
Capacity and overflow behavior are separate choices. A rejecting FIFO queue preserves all accepted items until removal and reports failure on a full insertion. An overwriting circular buffer deliberately discards its oldest item to retain the newest arrivals. These are different application programming interfaces. The existing representation independence example specifies the rejecting policy and gives its abstraction function and correctness argument against a shifting array implementation.
Circular buffer full and empty states 2026-10-18
With front-plus-count storage, empty means and full means ; every allocated slot is usable. With only the two wrapped array indices , the equality describes both possibilities. Indeed , and the two allowed counts have the same remainder. Recovering the count as therefore loses the full state. A count, a separate full flag, or a different index representation must resolve that ambiguity.
Another convention reserves one slot: for a physical array of length , empty means and full means . Its usable capacity is , and now recovers its count because . A five-slot array then stores at most four items.
This is the convention of the kernel's documented
CIRC_* helpers, which additionally require power-of-two array lengths. It is not a universal property of circular buffers; our capacity-five count-based example uses all five slots and does not use those helpers.[ref]Circular Buffers by David Howells and Paul E. McKenney. David Howells and Paul E. McKenney, Circular Buffers, What is a circular buffer?; Measuring power-of-2 buffers: full/empty convention and helper result range
Circular buffer indices Created 2026-10-18 Updated 2026-10-24
Let the capacity be a positive integer , and allocate exactly array slots . Store the front array index and count . The representation invariant isThe logical sequence and next-write array index areHere denotes the nonnegative remainder from modular arithmetic. The count determines which slots are live: leftover bits in other slots are not members of the FIFO queue. If , has no current front item; it identifies the position where the next successful insertion begins. If , points at a live item, so writing there requires an explicit overwrite policy.
For ,
enqueue(x) writes , then increments , leaving fixed. For , dequeue() saves and returns , then sets and . These are sequential updates, completed before the next call. A rejecting interface tests or first and returns full or empty, respectively, without changing state. peek returns only when ; size returns .One can instead store and , recovering . On insertion, advance and increment ; on removal, keep fixed and decrement . Thus the next-write array index is not necessarily the front array index.
Names such as
head and tail require checking the actual API: the kernel documentation calls the insertion position head and the removal position tail.[ref]Circular Buffers by David Howells and Paul E. McKenney. David Howells and Paul E. McKenney, Circular Buffers, What is a circular buffer?: head is insertion, tail is removal
The diagram is the state after step 8 of circular buffer operation trace. Reading the physical array from slot zero would give , which includes a removed label and puts first. Reading from for exactly items gives . The next insertion uses slot , not slot . This distinction also explains why the live sequence may need two contiguous pieces of the array: here slots through , followed by slot . No items need to move to make this wrap work.
Circular buffer operation costs 2026-10-18
With storage allocated in advance, each successful insertion, removal,
peek, size, and full/empty check uses worst-case time complexity in Big O notation. This assumes constant-cost indexed array access, bounded machine word index arithmetic and fixed-cost scalar item reads and writes. Each operation touches a bounded number of slots and fields regardless of and ; no shifting, scanning or resizing occurs. Rejected operations also take constant work. Producing a copy of all live items takes work under the same scalar-cost assumptions.The preallocated array occupies scalar slots plus constant bookkeeping. Allocation and any initial slot construction are separate setup costs. Copying a large item, running an expensive destructor or callback, allocating an item, or growing a dynamic array changes the operation cost and must be charged separately. The bound is worst-case structural work, with no need for amortized analysis, and says nothing about a measured wall-clock deadline.
These formulas describe sequential calls. Wrapping array indices implies neither atomic operations nor safe access from multiple software threads. Publication of an item's contents and reuse of its slot require a protocol justified under the relevant memory model, including any necessary acquire-release synchronization. The kernel documentation discusses barriers under explicit producer/consumer restrictions; Boost's documentation separately requires mutual exclusion for shared access involving writes. Neither property follows merely from choosing a circular buffer.[ref]
Circular Buffers by David Howells and Paul E. McKenney. David Howells and Paul E. McKenney, Circular Buffers, Using memory barriers with circular buffers; The producer; The Consumer: explicit synchronization and ordering requirements
[ref]Implementation. Jan Gaspar, Boost.Circular Buffer, Implementation, Thread-Safety: shared access involving a writer requires mutual exclusion
Circular buffer operation trace 2026-10-18
Start with , , and immutable labels. Perform
enqueue(A), enqueue(B), dequeue(), enqueue(C), enqueue(D), dequeue(), enqueue(E), enqueue(F), enqueue(G). The removals return and . The interleaved insertions and removals make the front advance before the array fills; slot reuse does not require waiting for the FIFO queue to become empty.An interleaved capacity-five FIFO queue trace branches at a full insertion
. The table follows completed sequential calls. At step 10, rejecting retains ; overwriting accepts and discards . Step 11 therefore returns different labels. The lower bars show membership lifetimes for and at operation boundaries, not elapsed times or programming-language object lifetimes. Details.At step 7, occupies physical slot and the next-write array index wraps to . At step 8, reuses slot , giving the physical layout in the preceding diagram. At step 9, replaces the stale contents of slot : the physical array is but the logical sequence is . Now , yet makes the state full rather than empty.
The displayed values are related by after every call. They are shown together to explain the invariant; an implementation need store only two. For the rejecting FIFO queue, every successful removal returns the earliest accepted label not already removed. The representation independence example supplies the general proof through its abstraction function; this trace makes the wrapped physical storage visible.
Counting sort Created 2026-10-13 Updated 2026-10-18
Counting sort uses integer sort keys in directly as indices into an array of counts. Constant-cost counting and indexing give time complexity in Big O notation. To retain complete sorting records, cumulative counts locate their output ranges; filling each range from its end while scanning the input backwards gives stable sorting. Emitting bare sort keys from counts would discard associated data. The term matters when the key range is large.[ref]
11.2 Counting Sort and Radix Sort. Open Data Structures: An Introduction by Pat Morin; Theorem 11.7 and stability
For an original worked example, take complete sorting records and possible integer sort keys:The letters label the associated data, and are never compared. Both input and output array indices start at zero, so their valid positions are ; the count array has array indices . Stable sorting must produceThe equal-sort key groups retain and in arrival order. Counting sort prefix ranges explain where those groups belong; stable record placement in counting sort explains how to fill them without losing that order or the complete sorting records.
Counting sort prefix ranges 2026-10-18
For sorting records with integer sort keys in , where , let count the input occurrences of sort key . Define two prefix sums:The empty sum gives . Here are counts of sorting records. Exactly sorting records have smaller sort keys, so the output positions for sort key form the half-open integer intervalIts right endpoint is excluded: the last occupied array index is when .[ref]
11.2 Counting Sort and Radix Sort. Open Data Structures: An Introduction by Pat Morin; §11.2.1 Counting Sort, paragraphs after Figure 11.7: running sums count keys at most k and determine the output block
For the seven-sorting record example, the frequency array, inclusive cumulative count array, and start array areThus sort keys zero, one, two and three occupy the integer intervals , , and , respectively. In particular, the three sorting records with sort key two belong at array indices three, four and five. The inclusive cumulative count counts all six sorting records with sort keys at most two; six is the exclusive endpoint, not the final position of a sorting record with sort key two.
These integer intervals fit together because for , and . Their lengths add to , so they partition all output positions without overlaps or gaps. An absent sort key contributes an empty integer interval: with frequencies , the endpoints are and sort key two has range . For empty input, every count and endpoint is zero; every reserved integer interval is empty. This makes the same formulas cover missing sort keys and the empty case.
Formal methods 2026-11-05
Formal methods use mathematically defined models, specifications and reasoning techniques to describe and analyze systems. They include writing a formal specification, proving properties through formal verification, exploring a finite model by model checking and deriving information through static program analysis. A method's guarantee is relative to its model and assumptions.
For example, a loop summing an array can maintain the invariant that the current accumulator equals the sum of the entries already visited. Initialization, preservation by one iteration and the exit condition establish the returned sum; a separate termination argument shows that an answer is reached. Arithmetic and array bounds must be specified, since a proof using unbounded integers need not describe overflowing machine arithmetic.[ref]
An Axiomatic Basis for Computer Programming by Tony Hoare. assignment and iteration rules, and reservations about arithmetic and termination
Hash table probe 2026-10-18
One inspection of an array slot during an open addressing operation. Its array index follows the chosen probing rule; its contents determine whether to continue, return a matching hash table record, or establish absence.
Linear probing 2026-10-18
An open addressing probe rule that visits modulo , using modular arithmetic, until a matching record or an appropriate stopping slot is found.[ref]
Lecture 10: Hashing III: Open Addressing. 6.006 Introduction to Algorithms, Fall 2011 by Massachusetts Institute of Technology, 6.006 course staff; Lecture 10: Hashing III: Open Addressing, p. 3, Linear Probing formula
For capacity , the hash table probe numbered , starting at zero, inspects the array indexThe hash function gives the first array index. Each subsequent hash table probe moves one slot forward, wrapping from to by modular arithmetic. Keep the hash function and key equality fixed while hash table records are stored.
Use three distinguishable slot states: occupied by a live hash table record; never used since initialization or rebuilding, written
E; or deleted, written D, a tombstone in open addressing. Lookup compares occupied hash table keys using key equality, stops at E, and continues past D.[ref]5.2 LinearHashTable: Linear Probing. Open Data Structures: An Introduction by Pat Morin; Section 5.2, three entry types and find: nil stops lookup, del does not
For an original example, take an eight-slot array, integer hash table keys, and the hash function . Start with all slots
E and insert the hash table records in that order. The insertion hash table probe traces, expressed as array indices, areThe resulting array holds hash table keys at array indices , with E in the other four slots. The first three hash table keys have the same initial array index, a hash collision; the fourth begins at but joins the same linear probing cluster. Lookup of examines array indices , rejecting before returning . Lookup of absent examines , including the stopping E: five hash table probes for a hash table load factor .Bound lookup to at most hash table probes, even if no
E remains. For algorithm termination, the first array indices are distinct: equality of and implies that divides by modular arithmetic, and forces . They therefore cover the entire finite set of slots exactly once. If no occupied hash table key matches and no E is encountered, exhausting these hash table probes establishes absence; wrapping around again supplies no new information.[ref]Lecture 10: Hashing III: Open Addressing. 6.006 Introduction to Algorithms, Fall 2011 by Massachusetts Institute of Technology, 6.006 course staff; Lecture 10: Hashing III: Open Addressing, pp. 1–2, probe permutation requirement and Search loop with m trials
The attraction is a compact array and a simple hash table probe rule. Consecutive slots can benefit from spatial locality, although actual memory cache behavior depends on representation and hardware. Correctness rests on preserving the search route; speed depends on how long that route becomes. The linear probing cluster below explains why even a moderate hash table load factor gives no universal constant lookup bound.
Linear probing cluster 2026-10-18
A maximal consecutive run of occupied or deleted slots in linear probing, bounded by never-used slots when any exist; adjacency wraps around by modular arithmetic. Deleted slots still belong to the search run.[ref]
5.2 LinearHashTable: Linear Probing. Open Data Structures: An Introduction by Pat Morin; Section 5.2.1, definition of a run using non-nil entries and modular indices
In the eight-slot example, array indices form one linear probing cluster. Inserting absent hash table key starts at , passes the occupied slots , and fills . This extends the same linear probing cluster despite having a different initial array index from . A hash collision at the initial slot is enough to make later slots compete too.
For a worst-case family, insert integer hash table keys into an empty capacity- array, using the hash function , where . They occupy array indices . Lookup of absent needs hash table probes. Taking gives hash table load factor at most while the number of hash table probes grows with . Thus occupancy alone cannot promise constant lookup work.
Before any rebuilding, bounded lookup, deletion by hash table key, and insertion with a duplicate check each have worst-case time complexity in Big O notation, assuming constant-cost hash function evaluation, array access and key equality. The counterexample gives a matching linear worst-case growth in the number of hash table probes. Hash table resizing is additional work; expensive string comparisons or hashing also fall outside these constant-cost assumptions.
Use the weekday number as an array index. Before adding dates, add one empty entry to each weekday before
start; otherwise the first date in those columns would appear a week too early. For day , the slot number is , whose remainder modulo 7 selects the column. The source's example makes clear that start counts empty weekday cells, rather than individual printed space characters.[ref]final class Column extends ArrayList<Data> {
private static final long serialVersionUID = 1L;
}
static String month(int start, int numDays) {
if (start < 0 || start >= 7 || numDays < 1 || numDays > 31)
throw new IllegalArgumentException("invalid month parameters");
String[] days = {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"};
int ncols = days.length;
List<Column> output = new ArrayList<Column>(ncols);
for (int c = 0; c < ncols; ++c) {
Column column = new Column();
column.add(new StringData(days[c], true));
output.add(column);
}
for (int c = 0; c < start; ++c)
output.get(c).add(new StringData("", true));
for (int d = 1; d <= numDays; ++d) {
int c = (start + d - 1) % ncols;
output.get(c).add(new IntegerData(d, false));
}
return printData(output);
}The two-character headers force every column to width two, and right justification aligns one-digit dates with the units digits of two-digit dates. Date appears in data row , below the header. For
month(1,30) the visible result isSu Mo Tu We Th Fr Sa
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30Solutions to all eight questions of the 18 June 2014 paper. The Scala fragments use the paper's Scala 2 style. Unless stated otherwise, array dimensions and array indices meet the supplied preconditions, and size calculations fit
Int.[ref]Imperative Programming examination paper by the University of Oxford (2014). Cover and pp. 2–15, Questions 1–8
For parts (b)–(e), use array indices as tree handles. This also makes the shared storage in part (d) type-correct: both pending handles and output markers occupy integer cells. Zero denotes a leaf of a rooted tree; a positive handle denotes a fork whose children are in Initialize
nodes[t-1]. Only finite acyclic structures are admitted. An allocated fork's children are themselves valid handles.CONST MaxForks = 100000;
TYPE Tree = INTEGER;
Node = RECORD left, right: Tree END;
Arena = ARRAY MaxForks OF Node;
VAR nodes: Arena;
used: INTEGER;
PROCEDURE NewFork(left, right: Tree): Tree;
BEGIN
ASSERT(used < MaxForks);
nodes[used].left := left; nodes[used].right := right;
INC(used);
RETURN used
END NewFork;
PROCEDURE Preorder(t: Tree; VAR out: ARRAY OF INTEGER;
VAR count: INTEGER);
PROCEDURE Visit(p: Tree);
BEGIN
IF p = 0 THEN
out[count] := 0; INC(count)
ELSE
out[count] := 1; INC(count);
Visit(nodes[p-1].left); Visit(nodes[p-1].right)
END
END Visit;
BEGIN
count := 0; Visit(t)
END Preorder;used := 0 before constructing any trees; NewFork allocates within this arena. The fixed capacity is an implementation parameter and may be replaced by a dynamically allocated arena. The returned prefix out[0..count) encodes Boolean values by and . This encoding is part of the chosen representation, not a conversion of a pointer into a Boolean value.The recursion invariant is that
Visit(p) appends exactly the preorder traversal of the subtree represented by p. If the full binary tree has forks, it has leaves; henceEach occurrence is visited once, using linear time complexity and a call stack bounded by the height of a binary tree. The arena is the input representation, not an extra copy made by the traversal.With the fixed array On success, , , and . On failure there is no occurrence; the output indices are unspecified. Thus
f visible to the procedures, use a Boolean result for success and two output array indices:TYPE Search = PROCEDURE(x: INTEGER; VAR i, j: INTEGER): BOOLEAN;Use a linear search over every pair of array indices.For a worst-case lower bound, consider an input in which every inspected element differs from . If an element remains uninspected, changing just that element to gives an indistinguishable execution requiring the opposite answer. Therefore a correct exact search algorithm must inspect all elements in the worst case.
PROCEDURE SearchAll(x: INTEGER; VAR i, j: INTEGER): BOOLEAN;
VAR r, c: INTEGER;
BEGIN
FOR r := 0 TO M-1 DO
FOR c := 0 TO N-1 DO
IF f[r,c] = x THEN i := r; j := c; RETURN TRUE END
END
END;
RETURN FALSE
END SearchAll;The printed phrase “at least ” uses the upper-bound notation for a lower-bound claim. Its precise interpretation isThis is an adversary argument for access to arbitrary independent array entries.
Maintain an integer The precondition is the abstract capacity condition. Appending at the first unused array index establishes in constant running time.
len, initially zero, with len = length xs:PROCEDURE Add(x: INTEGER);
BEGIN
ASSERT(len < N);
u[len] := x;
len := len+1
END Add;If
get(k) first applies a comparison sort to all entries, the comparison sorting lower bound gives at least comparisons in the worst case. A merge sort or heapsort meets that order, after which indexing costs constant time:This is a logarithmic factor worse than linear. The qualification to comparison sorting is necessary: merely saying that keys are integers does not prohibit counting sort for a bounded small range or radix sort under suitable machine-key assumptions. The printed assertion of an unavoidable superlinear bound needs the comparison model.



