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.Evaluate the mixed radix representation from its most significant end, using Horner's method. There is no need to compute the positional weights separately.The digit conditions are additional preconditions. The loop invariant at the loop test isAn empty sum is zero and an empty product is one, so initialization at is correct. For the next digit, writing giveswhich preserves the loop invariant. The loop variant is . At termination , givingThe code executes exactly multiplications, meeting the bound, and uses auxiliary space. When it returns zero. For the time representation in the source, it computes .
def digsToInt(xs: Array[Int], bs: Array[Int]): Int = {
require(xs.length == bs.length)
val N = xs.length
var i = N
var acc = 0
while (i > 0) {
i -= 1
acc = xs(i) + bs(i) * acc
}
acc
}A suitable precondition, including the empty array case, isA base of one is allowed: its only valid digit is zero. The empty product is one, so permits exactly . Each Euclidean division extracts the next least significant digit.The expensive precondition is a caller obligation, rather than a separately computed product inside the conversion. Define . The loop invariant istogether with for each processed digit. The Euclidean division identity proves preservation of the reconstruction equality. Positivity of the bases gives the digit bounds and the next bound on the quotient. The loop variant is . At exit, forces , henceThere are exactly remainder operations and divisions, hence counted operations. The returned array occupies space and the remaining auxiliary space is constant. A represented value can never exceed , as follows either by the same nested expansion or by telescoping ; thus the stated range is also necessary for positive bases.
def intToDigs(n: Int, bs: Array[Int]): Array[Int] = {
// Pre: every base is positive, and 0 <= n < product(bs).
val N = bs.length
val xs = new Array[Int](N)
var i = 0
var q = n
while (i < N) {
xs(i) = q % bs(i)
q = q / bs(i)
i += 1
}
xs
}Let be a saved mathematical copy of the original array. A precise postcondition for sorting isThe precondition is that
a is a non-null array of length . Its length and every unrelated object remain unchanged. The second clause preserves the array multiset, including all multiplicities; ordering alone would also permit overwriting the entire array with zeros. The saved is a specification device and need not be allocated by the implementation.Keep a running total in the mixin, and use only the inherited public operations. Updating after a successful superclass operation leaves the total unchanged if an empty For this trait mixed directly into
pop fails its precondition.trait SumPlugin extends Stack {
private var total = 0
override def push(x: Int): Unit = {
super.push(x)
total += x
}
override def pop(): Int = {
val x = super.pop()
total -= x
x
}
def sum: Int = total
}Stack, its representation invariant is , where is the abstract stack sequence. A successful push adds exactly its argument and a successful pop removes exactly its returned value. ThusThe mixin reads no private Stack fields. Its operations add constant time complexity. As in the paper's Int interface, an unbounded mathematical total requires excluding integer overflow, or replacing the accumulator with BigInt if the interface is allowed to change. A later mixin can transform a pushed value at a different point in the Scala trait linearization; the effect is analyzed in the next part.Maintain a cursor to the first unread marker. A fork consumes its own marker and then two complete encodings, in left-to-right order.Start with
PROCEDURE Decode(VAR code: ARRAY OF INTEGER;
VAR pos: INTEGER): Tree;
VAR marker: INTEGER; left, right: Tree;
BEGIN
marker := code[pos]; INC(pos);
IF marker = 0 THEN RETURN 0 END;
ASSERT(marker = 1);
left := Decode(code, pos);
right := Decode(code, pos);
RETURN NewFork(left, right)
END Decode;pos := 0; t := Decode(code, pos), with sufficient unused arena space. The valid-input precondition ensures that every read exists and every fork has two encoded subtrees. Evaluating the two recursive calls in separate statements fixes their order.Structural induction shows that decoding reconstructs the same binary tree shape and leaves the cursor just after its encoding. The tree handles need not coincide with the original handles: the abstract tree does. The result isThe time complexity is linear in the encoding length, with call stack space bounded by the height of a binary tree.
The representation invariant isEvery reachable non-
NIL pointer designates an allocated linked list node of the appropriate extension type. The header is not a data element. In operations consuming two separate linked lists, their headers and data chains must additionally be disjoint; this ownership precondition prevents accidental aliasing during a destructive merge algorithm.Extend the data linked list node type by a real number field and compare that field through an Oberon type guard.Construct the data chain from Only data nodes reach
TYPE RealNode = RECORD (Node) value: REAL END;
RealTail = POINTER TO RealNode;
PROCEDURE RealLess(p, q: Tail): BOOLEAN;
BEGIN
RETURN p(RealTail).value < q(RealTail).value
END RealLess;RealTail nodes, retain the ordinary Header for its header, set its count correctly, and callMergeSort(RealLess, p)RealLess, so the header needs no real-valued field. Its precondition is that every data node has the RealNode extension. With ordinary real numbers, the comparison supplies a total order; machine values such as NaN must be excluded if the machine floating-point number type admits them.Define an implicit conversion in the scope where client code uses the added methods:Then, for example,The Scala compiler can convert the receiver to
import scala.language.implicitConversions
implicit def stringToDist(s: String): DistFromString =
new DistFromString(s)val support = "Scala".getDom
val p = "Scala".getProb('a')DistFromString when the requested member is absent from String. The conversion must be imported or otherwise visible, and constructing the finite probability distribution retains the nonempty-string precondition of part (b).Split the half-open array segment at its midpoint, using one entry for the root:The precondition is . Set . For the child sizes are and , differing by at most one. The same construction in both subtrees proves subtree-size balance at every node. Structural induction also gives the inorder traversal , followed by , followed by , hence precisely ; no sortedness assumption is needed.
PROCEDURE MakeTree(VAR u: ARRAY OF INTEGER;
a, b: INTEGER): Tree;
VAR m: INTEGER; t: Tree;
BEGIN
IF a = b THEN RETURN NIL END;
m := a + (b-a) DIV 2;
NEW(t);
t.label := u[m];
t.left := MakeTree(u, a, m);
t.right := MakeTree(u, m+1, b);
RETURN t
END MakeTree;Exactly records are allocated. Each nonempty recursive call makes two child calls, so there are calls including empty segments. Under constant-cost allocation and reference parameter passing,For positive this is proportional to ; the empty case takes constant time. The call stack has depth.
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.Use an immutable list as private storage and cache its length. This makes the data refinement transparent without depending on library-specific dynamic array costs:The representation invariant is , with
class DocImpl extends Doc {
private var chars: List[Char] = Nil
private var length: Int = 0
def size: Int = length
def insert(x: Char, pos: Int): Unit = {
require(0 <= pos && pos <= length)
val parts = chars.splitAt(pos)
chars = parts._1 ::: (x :: parts._2)
length += 1
}
def delete(pos: Int): Char = {
require(0 <= pos && pos < length)
val parts = chars.splitAt(pos)
val old = parts._2.head
chars = parts._1 ::: parts._2.tail
length -= 1
old
}
def read(pos: Int): Char = {
require(0 <= pos && pos < length)
chars(pos)
}
}chars representing the abstract document sequence. splitAt(pos) isolates exactly the prefix before the selected array index-style position. Insertion retains that prefix and the old suffix, placing the new character between them. Deletion returns the old suffix head and retains its tail. Updating the cached length by one preserves the representation invariant. read and size preserve the abstract state.The preconditions permit insertion at the end, including position zero in an empty document, but forbid reading or deleting at the end.Represent a multiset by a pure multiplicity function. Every operation returns a new function describing updated counts:An empty instance is
class ContraMSet[-T](f: T => Int) {
def count(x: T): Int = f(x)
def insert(x: T): ContraMSet[T] =
new ContraMSet[T]((y: T) => f(y) + (if (y == x) 1 else 0))
def delete(x: T): ContraMSet[T] =
new ContraMSet[T]((y: T) => {
val old = f(y)
if (y == x && old > 0) old - 1 else old
})
def union[U <: T](that: ContraMSet[U]): ContraMSet[U] =
new ContraMSet[U]((y: U) => this.count(y) + that.count(y))
}new ContraMSet[T]((_: T) => 0). The constructor's precondition is that f is pure and gives a nonnegative integer count, with finite support of a multiset if only finite multisets are intended. Nonnegativity and the support condition are preserved by each operation. Integer overflow must be excluded for this fixed-width count interface.A contravariant type parameter makes
ContraMSet[T] a consumer of values of type T. Thus a counter able to answer queries for every animal can also answer queries restricted to cats. It does not expose or enumerate stored T values. The return type ContraMSet[T] is legal: its outer covariant result position and the class's contravariant argument position together place negatively.For
union, the argument itself is in a negative position, while its ContraMSet argument would reverse that sign. Using ContraMSet[T] directly as the parameter type would therefore put positively and fail the variance-position rule. Introduce : both operands can count every U, and their sum is a ContraMSet[U]. This is a restriction to a common query domain; operations do not promise to preserve counts outside the narrowed domain.[ref]Scala 2.11 basic declarations and definitions specification. Type Parameters, Variance Annotations; bounds and sign changes
Pointwise evaluation givesNo old function is modified, so updates preserve immutable object. Constructing each wrapper takes constant work, but a later
count may traverse a chain of previously created programming closures; its running time need not be constant in the history length. This functional representation trades enumeration and direct storage of occurrences for composable counting behavior.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 the paper's 26-letter alphabet, use the Oberon Both cases of a letter map to the same uppercase letter before subtraction, so the result lies in
CAP operation and contiguous character codes:PROCEDURE Index(c: CHAR): INTEGER;
BEGIN
RETURN ORD(CAP(c)) - ORD("A")
END Index;0..25. This relies on the ordinary Latin A–Z and a–z character encoding used by Oberon; it is not a Unicode case-folding algorithm. The stipulated letter-only precondition avoids any need to reject punctuation.Generate each reciprocal factorial from the preceding one, instead of recomputing a factorial for every summand:The loop invariant is , , and . Initially and . The division produces , which is then added to the previous partial sum. The loop variant is . Consequently the returned value is , using exactly divisions and additions, no multiplications, and time complexity. The slightly weaker precondition also permits the useful zero-term boundary case. For
def eb(n: Int): Double = {
require(n >= 0)
var i = 0
var term = 1.0
var sum = 1.0
while (i < n) {
i += 1
term /= i.toDouble
sum += term
}
sum
}Double, small positive terms eventually cease to change sum because of floating-point rounding.Return the lower-bound search boundary, permitting both zero and the array length:The precondition includes nondecreasing order. The loop invariant isInitially both excluded ranges are empty. If , sortedness justifies excluding every position through
def lowerBound(a: Array[Int], x: Int): Int = {
require(a != null) // a is nondecreasing
var lo = 0
var hi = a.length
while (lo < hi) {
val mid = lo + (hi - lo) / 2
if (a(mid) < x) lo = mid + 1
else hi = mid
}
lo
}mid; otherwise it justifies excluding every position from mid onwards. The loop variant is a nonnegative integer that strictly decreases. When the old width is positive, mid is a valid array index. The midpoint expression avoids adding two potentially large indices. At exit , so lo satisfies the required partition, including when . Each new width is at most half the old width, rounded down; this gives time complexity and constant space, conventionally for nontrivial sizes.Black-box testing chooses inputs and expected outputs from the public specification without using the implementation's control structure. Equivalence class testing partitions the input space into classes expected to exercise similar specified behavior, then chooses representatives; boundary cases test where that behavior changes. For equal-range search, use these concrete representatives:
- Empty array:
([], 4)gives(0,0). - One entry, with the target below, equal, and above it:
([4],3),([4],4),([4],5)give(0,0),(0,1),(1,1). - An absent interior value:
([1,3,5],4)gives(2,2). - One interior match:
([1,3,5],3)gives(1,2). - A repeated interior block:
([1,3,3,3,5],3)gives(1,4). - A repeated block touching the left or right boundary:
([3,3,5],3)and([1,3,3],3)give(0,2)and(1,3). - Every entry equal:
([3,3,3],3)gives(0,3); targets two and four give(0,0)and(3,3). - Extreme keys:
([Int.MinValue,0,Int.MaxValue],Int.MaxValue)gives(2,3), and searching forInt.MinValuegives(0,1).
For each result independently check , the strict inequalities outside the returned interval, and equality inside it. Unsorted inputs violate the precondition; they are not ordinary members of the specified valid-input classes. Software testing supplies evidence about implementations, while the loop invariant proof explains correctness for every valid input.
The runtime precondition check rules out negative dimensions:Both links point to the fresh list header, so all reciprocal-link conditions hold and there are no ordinary entries to violate ordering or nonzero-value requirements. The abstraction function therefore yields the zero vector of dimension
def makeZeroVector(size: Int): Node = {
require(size >= 0)
val h = new Node(0.0, size)
h.prev = h
h.next = h
h
}size. Construction uses constant time and constant space.Assume The search loop invariant says that the structure is unchanged and every ordinary linked list node passed has index smaller than
vector already satisfies the full representation invariant in part (b). Check the local bounds and value preconditions, then find the first index not smaller than the requested one:def setElement(vector: Node, value: Double, index: Int): Unit = {
require(vector != null)
require(0 <= index && index < vector.index)
require(value != 0.0 && !java.lang.Double.isNaN(value) &&
!java.lang.Double.isInfinite(value))
var p = vector.next
while ((p ne vector) && p.index < index) p = p.next
if ((p ne vector) && p.index == index) p.value = value
else {
val left = p.prev
val fresh = new Node(value, index, left, p)
left.next = fresh
p.prev = fresh
}
}index; p is the next candidate. The number of unpassed ordinary entries is a loop variant. The short-circuit evaluation prevents testing the header index as an ordinary entry. If an equal index exists, changing only its nonzero value preserves the representation invariant. Otherwise left and p delimit the unique insertion position: initializing both new links before redirecting their neighbors preserves reciprocal links and strict index order. Exactly the requested coordinate changes. With stored entries, the search costs and the splice costs constant time; checking the whole input structure is a caller obligation, rather than an unmentioned scan inside each call.