Loop variant 2026-10-18
A decreasing measure used to prove algorithm termination. A nonnegative integer loop variant must strictly decrease after every completed body whose condition was true; the body itself must terminate.[ref]
Loop invariants. CS 2112 Fall 2019; Loop invariants, Using loop invariants to show code is correct: Termination and the decrementing function
The prefix maximum correctness example uses . It counts unread entries and is independent of whether the new entry changes the maximum. Ties therefore cannot stall progress: increases even when stays the same. An integer bounded below cannot strictly decrease indefinitely, which supplies the missing algorithm termination argument.
Control flow and four proof obligations for the prefix maximum correctness example
. Initialization, preservation, and exit establish partial correctness; the loop variant establishes algorithm termination and hence total correctness. The loop invariant is restored after both variable assignments. The input array stays unchanged throughout. Details. Lower-bound binary search invariant 2026-10-31
In a nondecreasing array, the first position whose entry is at least is found by maintaining a half-open unresolved interval . Entries before are smaller than ; entries from onward are at least . Initially . At midpoint , replace by if , and otherwise replace by . Monotonicity preserves the loop invariant, and decreases. On exit is the first qualifying position, or if no such entry exists. Repeated values and endpoint cuts require no additional case.
Mathematical induction Created 2026-09-24 Updated 2026-10-14
A mathematical induction proof establishes a property for every integer by proving its induction base case and its inductive step. If a counterexample existed, the well-ordering principle would give a least one; its predecessor and the inductive implication exclude it. The starting case excludes a counterexample at .
The useful idea is to cover infinitely many cases with two finite pieces of reasoning. The induction base case starts the chain, and the inductive step shows that success at an arbitrary stage propagates to the next. It is especially natural when a construction adds one item at a time: another row of a diagram, another input processed by an algorithm, or another term of a sequence. The loop invariant method is the same propagation idea applied to successive stages of a computation.
For the triangular number , the proposed formula is . At , both sides are one. Assuming the formula for a fixed positive integer , add the next row:This is the desired formula at , proving the inductive step. For example, and ; these numerical cases illustrate the general step rather than replace it.
Adding the next row gives the induction step; rearranging two triangular arrays gives the same formula geometrically
. Details. The second picture explains where the formula comes from: two copies of the triangular arrangement tile an by rectangle. Thus . The mathematical induction argument certifies how the formula continues, while the rearrangement makes its factors visible. Neither picture is restricted to the five rows drawn: both constructions are specified for every positive integer .
Merge algorithm Created 2026-10-13 Updated 2026-10-18
The merge algorithm combines two finite sorted sequences under the same total order, retaining every entry exactly once. Keep a position at the first unconsumed entry, or "head", of each input. While both heads exist, compare their keys, append the smaller head to the output, and advance only that head's position. On equality take the left head. Once either sequence is exhausted, append the other remainder without further key comparisons. Empty inputs therefore require no head comparisons. The MIT notes illustrate this two-head combination step.[ref]
Lecture 3: Insertion sort, merge sort. 6.006 Introduction to Algorithms, Fall 2011 by Massachusetts Institute of Technology, 6.006 course staff; MIT 6.006 (2011), lecture 3: merging sorted arrays
Five head comparisons in the merge algorithm
. Orange heads are chosen and arrows lead to the next output entry. The compared head pairs are (1,2), (4,2), (4,3), (4,8) and (7,8). The left sequence then runs out; append (8,9) without further key comparisons. Details.The head pairs , , , and select respectively. After the fifth comparison the left sequence is empty, so is appended. Seven entries are written, but only five head comparisons occur: tests for exhaustion and appending the remainder are separate operations. This distinction makes the example's count smaller than the bound .
For nonempty inputs of lengths , each head comparison consumes exactly one entry. After such removals at most one entry remains, so both heads cannot still exist. Hence the merge algorithm uses at most head comparisons. The bound can be attained: and require six head comparisons before the left sequence is exhausted. With constant-cost comparisons, access and copying, writing all entries gives time complexity in Big O notation. Writing a separate output array needs entry slots; the two positions require only constant additional storage.
The merge algorithm is useful whenever two already ordered collections must become one: it reads each input only forwards. This works with arrays and linked lists, without needing access to arbitrary middle positions. Choosing left on ties preserves each input's internal order and places equal left entries before equal right entries, which is the combination rule needed for stable sorting in merge sort.
The loop invariant behind algorithm correctness is that the emitted prefix is sorted, contains exactly the consumed entries, and every emitted key is at most every unconsumed key. Each sorted sequence has its smallest remaining key at its head, so the smaller of the two heads is safe to append. The invariant survives each removal; when one sequence ends, the other sorted remainder is safe to append in full. Each comparison removes an entry, so termination follows from the finite input lengths.
// Target: computer-science.bigb
Modular exponentiation Created 2026-10-06 Updated 2026-10-20
Modular exponentiation computes the remainder for an integer base , a nonnegative integer exponent , and, here, an integer modulus . It applies exponentiation by squaring while reducing after each multiplication, including each squaring. The result is the unique representative in . There is no requirement that the base be coprime to the modulus when the exponent is nonnegative.[ref]
A Computational Introduction to Number Theory and Algebra by Victor Shoup. Section 3.4, pp. 64–66: canonical representatives, modular products, and nonnegative-exponent repeated squaring
Take , , and . Initialize ; the remaining digits giveHence . The final operation multiplies by , since the preceding square has already been reduced. The computation never needs to construct .
In general reduce the base first, . For , initialize and process the remaining binary digits with , followed, for a digit one, by . The loop invariant iswhere is the processed prefix value. The congruence is preserved because modular arithmetic respects multiplication: if , then and . Thus changing representatives never changes the final congruence class.
Both factors in every product are less than , so the unreduced product is less than , and the stored result is again less than . The same bounds hold for a gigantic exponent, such as : there are squarings, each on bounded-size residues. For base two, the corresponding ordinary power would have binary digits. The benefit is avoiding the huge ordinary power altogether, rather than constructing it and taking one final remainder. Initial reduction is essential when the supplied base is much larger than the modulus.
For , return under the same empty-product convention as exponentiation by squaring. For , require , find the modular multiplicative inverse , and compute ; the extended Euclidean algorithm can find that inverse. Its cost is separate from the positive-exponent binary powering multiplication count. For example, , while is undefined because and are not coprime.
A reversible computation gives the coherent quantum modular exponentiation used in quantum order finding and discrete-logarithm Fourier sampling.
Partial correctness Created 2026-10-18 Updated 2026-10-24
Partial correctness means that every terminating execution begun in a program state satisfying the precondition finishes in a program state satisfying the postcondition . For a sequential program , the usual Hoare triple notation isHere the notation expresses partial correctness; it makes no promise that finishes. The precondition and postcondition describe permitted starting circumstances and required final circumstances, rather than tests performed automatically by the program.[ref]
An Axiomatic Basis for Computer Programming by Tony Hoare. Hoare, 1969, §3 Program Execution, p. 577, and §4 General Reservations, pp. 578–579; the paper instead labels the precondition P, program Q and result R
For a sharp example, take the precondition and the following sequential program over mathematical integers:The condition remains true, so execution never reaches the instruction after the loop. Consequently its partial correctness claim holds for the postcondition , and even for the impossible postcondition : there is no terminating execution with a wrong final result. This is vacuous truth, not evidence that the loop computes a sum. The same program fails total correctness for this permitted input.
i = 0
s = 0
while i < n:
i = iThe distinction is useful in a proof: first show that an answer, whenever produced, is right; then establish algorithm termination. The summation loop verification supplies both arguments for a loop that actually progresses. Its finite trace illustrates one execution, while its loop invariant covers every permitted input.
Store the accepted but undelivered values in an ordinary FIFO queue, owned exclusively by the Communicating Scala Objects process. An input event is enabled precisely when the FIFO queue is not full, and an output event precisely when it is not empty. An output event's expression is evaluated only after that event is selected; its epilogue runs after the output completes. The following uses no buffered communication message-passing channels.
def strictBuffer[T](capacity: Int, in: ?[T], out: ![T]): PROC = proc {
require(capacity > 0)
val q = scala.collection.mutable.Queue[T]()
val poll = 1000000L // nanoseconds
try {
while (out.canOutput && (in.canInput || q.nonEmpty)) {
alt(
(q.size < capacity && in) =?=> { x => q.enqueue(x) }
| (q.nonEmpty && out) =!=> { q.front } ==> { q.dequeue(); () }
| after(poll) ==> { () }
)
}
} catch { case _: io.threadcso.process.Stopped => () }
finally { in.closeIn; out.closeOut }
}Here
canInput and canOutput mean that the port has not become permanently unable to communicate; they are not instantaneous readiness tests. A closed input event is disabled by the Communicating Scala Objects guarded alternation. The timer periodically rechecks closure even when the affected port's data event is disabled, for example when the FIFO queue is empty and the downstream port closes. It neither transfers data nor imposes a deadline on a healthy producer. This avoids silently assuming that closure of a disabled event wakes an alternation. The relevant output-event and closure conventions are documented in [ref].Write and for the input and output sequences. The loop invariant is that is followed by the contents of the FIFO queue. An input appends one value to its rear; an output removes exactly its front. Consequently,The two data events remain offered whenever their respective size conditions hold. Under a fair choice policy, an endlessly ready event is eventually selected. The timer requires the usual assumption that expiry cannot starve a ready communication forever.
When the input closes, no more values are accepted, but all remaining values stay eligible for output. Once the FIFO queue empties, the process closes its output and terminates. If the output closes first, there is no recipient for those remaining values: the process abandons them, closes the input to release the producer, and terminates. Draining after upstream closure requires a consumer that continues receiving. The positive-capacity precondition is deliberate: two separate synchronous message passing events cannot provide a capacity-zero buffer with zero discrepancy after every event; that would require a simultaneous three-party transfer.
Order the edges arbitrarily and fix their colours one at a time. After a partial assignment , regard all unassigned edges as independent fair red/blue choices, and computeThe conditional expectation of a monochromatic clique count is easy to compute. For each four-vertex subset , let be the number of its six edges still unassigned. Its contribution to isIn the second case, every remaining edge must match the already fixed colour; this includes a completely fixed monochromatic copy, for which and the contribution is one. In the last case, the six unassigned edges may become either all red or all blue. By linearity of expectation,For the next unassigned edge , compute and . The law of total expectation givesChoose the colour with the smaller value, resolving a tie by a fixed rule. At least one is at most their average, so the loop invariant is that never increases. Initially ; once every edge is fixed, there is no randomness left and . Therefore the method of conditional expectations guaranteesA direct implementation recomputes both sums over all four-vertex subsets for each of the edges, requiring arithmetic operations. The comparisons can be exact: every contribution is a multiple of , so store as an integer with bits. This gives bit complexity with elementary arithmetic, and hence a deterministic polynomial time construction. No random sampling is performed by this derandomization.
Use binary search. Maintain an inclusive candidate interval
[lo,hi], initially [0,n-1]. Compare the target v once with the middle entry; a ternary comparison returns less than, equal to, or greater than. Equality ends successfully. Otherwise sortedness permits discarding the middle entry and the entire half on the wrong side:lo := 0; hi := n-1;
WHILE lo <= hi DO
mid := lo + (hi-lo) DIV 2;
IF v = S[mid] THEN RETURN TRUE
ELSIF v < S[mid] THEN hi := mid-1
ELSE lo := mid+1
END
END;
RETURN FALSEThe two written tests represent one ternary comparison whose result is then inspected. The loop invariant says that every occurrence of
v, if any, lies in the candidate interval. Initially it is true. The order comparison and sortedness preserve it after either discard. If the interval becomes empty, the loop invariant proves absence.A failed comparison reduces a candidate interval of size to at most . For , at most comparisons are needed; an empty input needs none. HenceThe claim counts comparisons. Indexed access to an array also makes elapsed running time logarithmic in the usual model; a linked list representation would not give the same access cost.
Use two indices, initially the first and last positions of the ordered sequence. The two elements occupy distinct positions, so continue only while
i < j:i := 0; j := n-1;
WHILE i < j DO
t := S[i] + S[j];
IF t = x THEN RETURN TRUE
ELSIF t < x THEN i := i+1
ELSE j := j-1
END
END;
RETURN FALSEThe loop invariant is: a solution exists in the original input if and only if one exists with both indices in
[i,j]. If S[i]+S[j] < x, then S[i]+S[k] <= S[i]+S[j] < x for every k <= j, so index i cannot belong to a remaining solution. If the sum is too large, S[k]+S[j] >= S[i]+S[j] > x for every k >= i, so index j cannot belong to one. This proves preservation of the loop invariant. Equality gives the desired pair, and an interval with fewer than two positions contains none.Every unsuccessful iteration decreases the loop variant
j-i by one, so there are at most sum comparisons and only constant space is needed. Duplicate values are permitted if they occupy distinct positions. Under exact constant-cost real arithmetic and endpoint access,For nonnegative integer
x and positive integer y, initialize the quotient to zero and subtract one divisor at a time:q := 0; r := x;
WHILE r >= y DO
r := r-y;
q := q+1
ENDThe loop invariant is , , , with
x and y unchanged. It is true initially. A body execution changes to and keeps nonnegative because of the guard. The nonnegative integer loop variant strictly decreases by , so termination follows. At exit the guard supplies ; henceThere are exactly body executions. Under the unit-cost model, the time complexity including initialization and the final test is , which is for integer . This is a bound in the value of x, rather than in its encoded bit length.In binary long division, first double the divisor until it exceeds
x, then halve it successively. Keep p = 2^j and d = y*p, so no exponentiation is needed inside a loop:q := 0; r := x;
j := 0; p := 1; d := y;
WHILE d <= x DO
d := 2*d;
p := 2*p;
j := j+1
END;
WHILE j > 0 DO
d := d DIV 2;
p := p DIV 2;
j := j-1;
IF r >= d THEN
r := r-d;
q := q+p
END
ENDThe first loop invariant is , , , and . Every doubling preserves it. For integer inputs, after at most iterations when ,
d > x; if , this loop has no iterations. The unbounded increase of proves termination. Equivalently, one can use the decreasing nonnegative loop variant while the guard is true.At the top of the second loop the loop invariant isIt holds on entry because
d > x = r. Suppose the old d is and the old p is . After the three halving assignments, and the old remainder satisfies . If , the desired new bound already holds. Otherwise subtracting leaves and adding to q preserves because . The divisions by two are exact: before each halving, j > 0 makes both p and d even.The loop variant
j decreases by one each iteration. At exit , so d = y and the loop invariant gives the required remainder bound. The work per iteration is constant in the unit-cost model, and the two loops have the same number of iterations. For ,The case , including , takes constant time. These are arithmetic-operation counts with sufficient range for the doubled values; arbitrary-precision arithmetic incurs additional bit complexity. If the inputs were arbitrary positive real numbers, the uniform bound would depend on rather than only ; the stated is the conventional integer division interpretation.The loop invariant in binary long division says exactly where the true integer quotient lies:Halving
p proposes the midpoint q+p of this candidate interval. The comparison r >= y*p is equivalent to (q+p)*y <= x, so it decides whether the quotient is in the upper or lower half. Adding p chooses the upper half; leaving q unchanged chooses the lower half. Thus binary long division is binary search for the last integer satisfying , deciding the quotient's binary numeral system digits from most significant to least significant. The doubling loop first finds a power-of-two interval containing that quotient.Replace the recursive transfer in part (b) by simultaneous state updates, using a temporary variable to preserve the old
a:PROCEDURE FibIter(n: INTEGER): INTEGER;
VAR k, a, b, oldA: INTEGER;
BEGIN
k := n; a := 1; b := 0;
WHILE k > 1 DO
oldA := a;
a := a + b;
b := oldA;
k := k - 1
END;
RETURN a
END FibIter;For original input , a loop invariant isIt holds initially because . Each iteration preserves it by the identity from part (b). The loop variant is , a nonnegative integer that decreases by one whenever the guard holds. On exit , so the loop invariant gives , exactly the returned answer. The procedure uses arithmetic operations and stored integer variables, without depending on compiler treatment of tail calls.
Represent the two recursively split arrays by adjacent half-open ranges of the original array. This avoids allocating a fresh pair at every call. The caller supplies a distinct work array with at least as many elements as
a; only that work array is used for merging. The Oberon code isPROCEDURE SortRange(VAR a, work: ARRAY OF INTEGER;
l, r: INTEGER);
VAR m, i, j, k: INTEGER;
BEGIN
IF r-l > 1 THEN
m := l + (r-l) DIV 2;
SortRange(a, work, l, m);
SortRange(a, work, m, r);
i := l; j := m; k := l;
WHILE (i < m) & (j < r) DO
IF a[i] <= a[j] THEN
work[k] := a[i]; i := i+1
ELSE
work[k] := a[j]; j := j+1
END;
k := k+1
END;
WHILE i < m DO
work[k] := a[i]; i := i+1; k := k+1
END;
WHILE j < r DO
work[k] := a[j]; j := j+1; k := k+1
END;
k := l;
WHILE k < r DO
a[k] := work[k]; k := k+1
END
END
END SortRange;
PROCEDURE MergeSort(VAR a, work: ARRAY OF INTEGER);
BEGIN
SortRange(a, work, 0, LEN(a))
END MergeSort;For a range of length , the left length is and the right length is , so as required. Both are positive and smaller than , giving the recursive loop variant analogue for algorithm termination. Empty ranges are also harmless, although the question supplies a nonempty array.
For the first merge loop, regard the two sorted ranges as fixed input sequences and . They are not overwritten during the merge. A loop invariant is that , , , and
work[l:k) is the stable sorted merge of the consumed prefixes a[l:i) and a[m:j). Every emitted element is at most every remaining element. Taking the smaller head preserves this property, and choosing the left head on equality preserves stable sorting. The loop variant isIt is nonnegative and decreases by one per iteration. On exit at least one range is exhausted.For the second loop, the remaining left range is already sorted and any right remainder is absent whenever that loop runs. Appending the next left element preserves the same merge loop invariant; its loop variant is . For the third loop, the left range is exhausted and the right remainder is appended in order; its loop variant is . On completing both loops,
work[l:r) is the complete sorted merge and .In the final copying loop, the loop invariant is that
a[l:k) equals work[l:k), work[l:r) remains the correct sorted answer, and positions outside a[l:r) are unchanged. Its loop variant is . At exit the whole answer has been copied back. By strong induction on range length, the recursive calls sort their halves, and the merge preserves the original multiset. ThusThe work array must not alias a: overwriting the source while merging would invalidate the merge loop invariant.Use an Oberon record containing an active coefficient count and a bounded array of coefficients in ascending power order. A count of zero represents the zero polynomial; a count represents . Trailing zero coefficients are allowed, so the count need not be the degree of a polynomial plus one.
CONST Capacity = 1024;
TYPE Polynomial = RECORD
len: INTEGER;
coeff: ARRAY Capacity OF REAL
END;
PROCEDURE Horner(VAR p: Polynomial; x: REAL): REAL;
VAR i: INTEGER; value: REAL;
BEGIN
i := p.len; value := 0.0;
WHILE i > 0 DO
i := i-1;
value := p.coeff[i] + x*value
END;
RETURN value
END Horner;The representation invariant is , with only indices below
len significant. The parameter is passed by reference to avoid a full record copy; the procedure does not modify it.Let be the original coefficient count. At each guard test the loop invariant isThe empty sum is zero initially. If the old index is , after decrementing and updating,which preserves the loop invariant. The loop variant is , decreasing by one until zero. At exit,This is exactly the right-to-left nesting of Horner's method, matching the supplied right fold. It uses arithmetic operations and additional scalar storage.
REAL gives ordinary floating-point evaluation; the displayed identities specify the ideal arithmetic calculation, with the usual rounding in an actual floating-point implementation.Add polynomials by adding equal-power coefficients, interpreting a missing coefficient as zero. Compute into a local record before assigning the output, so the result may safely be the same actual record as either input.
PROCEDURE Add(VAR p, q, result: Polynomial);
VAR t: Polynomial; i: INTEGER; u, v: REAL;
BEGIN
t.len := p.len;
IF q.len > t.len THEN t.len := q.len END;
i := 0;
WHILE i < t.len DO
u := 0.0; v := 0.0;
IF i < p.len THEN u := p.coeff[i] END;
IF i < q.len THEN v := q.coeff[i] END;
t.coeff[i] := u+v;
i := i+1
END;
result := t
END Add;With , the loop invariant is and, for ,
t.coeff[k] is the sum of the two extended input coefficients at . The loop variant is . The inputs remain unchanged throughout the loop, and at exit every active coefficient is correct. The time complexity is for the active arithmetic; assigning the fixed-capacity record can additionally copy its full capacity.For the product, each pair of input terms contributes . Initialize the active result to zero, then accumulate these pairwise contributions:
PROCEDURE Multiply(VAR p, q, result: Polynomial);
VAR t: Polynomial; i, j, k: INTEGER;
BEGIN
t.len := 0;
IF (p.len > 0) & (q.len > 0) THEN
t.len := p.len + q.len - 1
END;
(* Precondition: t.len <= Capacity. *)
k := 0;
WHILE k < t.len DO
t.coeff[k] := 0.0; k := k+1
END;
IF t.len > 0 THEN
i := 0;
WHILE i < p.len DO
j := 0;
WHILE j < q.len DO
t.coeff[i+j] := t.coeff[i+j]
+ p.coeff[i]*q.coeff[j];
j := j+1
END;
i := i+1
END
END;
result := t
END Multiply;The input representation invariants and the capacity precondition are assumed. No coefficient is accessed when either input is the zero polynomial. The initialization loop invariant says coefficients below are zero; its loop variant is
t.len-k.For positive input lengths , the outer loop invariant says that and each coefficient equalsThe inner loop invariant adds the contributions with and to that sum, while . The update adds exactly the next missing term at index , preserving the inner loop invariant. The loop variants are for the outer loop and for the inner loop. On exit all pairs have contributed, yieldingThe product uses arithmetic operations and a temporary coefficient array of capacity
Capacity. Both operations allow trailing zeros; no extra normalization loop is required. Only the active coefficients are meaningful, so inactive record entries are never read by these operations. As in part (a), floating-point coefficients implement these formulas subject to rounding.Normalize each word once by applying
toLower to its characters. Maintain an association list whose keys are distinct normalized words and whose values are their frequencies:import Data.Char (toLower)
type TextWord = String
type Text = [TextWord]
type Frequency = Integer
type Entry = (TextWord, Frequency)
count :: Text -> [Entry]
count = foldl addWord [] . map (map toLower)
where
-- Increment the unique matching key, or append a new key with count one.
addWord :: [Entry] -> TextWord -> [Entry]
addWord [] w = [(w, 1)]
addWord ((v, n):rest) w
| w == v = (v, n + 1) : rest
| otherwise = (v, n) : addWord rest wAfter any input prefix, each normalized word seen so far occurs exactly once as a key, with its number of occurrences as value. This loop invariant holds initially for the empty association list. A step either increments the unique existing entry or introduces exactly one new entry, so mathematical induction provesfor every word that occurs. In particular,
Baa and baa contribute to the same key baa; the three frequency-two keys in the source example are baa, yes and sir.The association list preserves first-occurrence order, although the specification allows any order here. For input words and distinct normalized words, it performs key comparisons in the worst case. Character normalization costs the total input character count, and a string comparison can inspect multiple characters. Characterwise
toLower supplies the intended case-insensitive comparison convention for the example; full Unicode case folding can require a different normalization policy.P := I OR E
repeat:
Q := P OR boolean_product(P, E)
if Q = P: return P
P := QAt the start of each iteration, for the current length bound . The update produces , proving the loop invariant by mathematical induction. If , every edge extension of a known graph path is already known. Repeating the same update then makes no further change, so every longer graph path is represented as well.
Every reachable pair of distinct vertices has a simple path of at most edges: a repeated vertex allows the intervening part to be deleted. Thus the process stabilizes after finitely many updates and returnsThis is the reflexive transitive closure. An alternative stopping rule is to stop once the length bound is at least , avoiding a final equality-detection iteration. For or , already suffices.
Use Kahn's algorithm for topological sorting. With an adjacency list representation, count every vertex's indegree by scanning the edges. Put all zero-indegree vertices in a queue. Repeatedly remove one such vertex , append it to the output, and delete its outgoing edges by decrementing the stored indegree of each neighbour; enqueue a neighbour exactly when its count becomes zero.
The loop invariant is that stored counts are the indegrees in the unreported subgraph and the queue contains exactly its unreported zero-indegree vertices. A nonempty finite Directed acyclic graph always has such a vertex: otherwise following incoming edges would eventually repeat a vertex, creating a directed graph cycle. Thus the queue cannot become empty while any vertex remains.
For every original edge , cannot enter the output until that edge has been removed by outputting . ThereforeEach vertex enters and leaves the queue once, and every edge is scanned a constant number of times, giving time and auxiliary space beyond the adjacency lists. If given only an adjacency matrix, scanning neighbours instead takes time.


