Replace the tail call by updates to local bounds and a local rank:The loop invariant says that , , and selecting rank
def selectIterative(k: Int, l: Int, r: Int): Int = {
require(0 <= l && l < r && r <= a.length)
require(0 <= k && k < r - l)
var low = l
var high = r
var rank = k
while (high - low > 1) {
val p = partition(low, high)
val pivotRank = p - low
if (rank == pivotRank) return a(p)
if (rank < pivotRank) high = p
else {
rank -= pivotRank + 1
low = p + 1
}
}
a(low)
}rank in the active segment yields the originally requested order statistic. The original segment's multiset is preserved, and outside it no entries change. The updates are exactly the recursive reductions proved in part (c). The loop variant is ; a non-returning iteration strictly decreases it. A one-entry segment has only rank zero, establishing the final return. Auxiliary space is , rather than a potentially deep call stack.Test singleton and two-element segments, first/last/interior requested ranks, nonzero segment offsets, already sorted and reversed inputs, random inputs, all-equal inputs and several patterns of duplicates. Include
Int.MinValue and Int.MaxValue to expose overflow-prone comparisons. An independent sorted copy supplies the value oracle; also check preservation of the segment's multiset and of all outside entries. For speed tests, increase segment size and include deliberately unfavorable pivot arrangements, rather than timing random inputs alone.Distinct entries do not guarantee linear worst-case time: the worst case is . A median of three can repeatedly be the second-smallest remaining value, leaving a segment of size when seeking its maximum. The quadratic median-of-three selection family gives explicit distinct inputs realizing this behavior for the implementation above; one six-entry member is . Its recurrence is , yielding a quadratic sum. Balanced reductions instead give . Randomized or suitable average-case analyses give expected linear selection, but this deterministic heuristic alone supplies no such worst-case guarantee. Unlike Quicksort, Quickselect explores only one side, so a balanced run is linear rather than .
The abstract state is a finite multiset of ordered values, initially empty. Multiplicity matters: deleting the minimum removes one occurrence, not every equal occurrence. Assume a consistent total order on This abstract data type places no fixed-capacity bound on insertion. The only operation requiring a nonempty collection is
T.trait PriorityQueue[T] {
// Pre: true. Post: Q' is the empty multiset.
def clear(): Unit
// Pre: true. Post: Q' = Q with one additional occurrence of x.
def insert(x: T): Unit
// Pre: Q is nonempty.
// Post: result is a minimum of Q;
// Q' is Q with exactly one occurrence of result removed.
def delMin(): T
// Pre: true. Post: result iff Q is empty, and Q' = Q.
def isEmpty(): Boolean
}delMin. The representation must preserve duplicates even if it uses a data structure commonly associated with sets.Not automatically: the duplicate-value policy must implement a multiset. A strict binary search tree storing one entry per distinct key represents a set; inserting a second equal value then loses a required occurrence. A fixed rule that puts all equal values on one side also needs care, because ordinary balancing rotations need not preserve that one-sided equality rule.
One valid refinement stores a multiplicity at each distinct key. Inserting an equal key increments its count;
delMin decrements the minimum key's count and removes that node only at zero. Alternatively, use distinct internal keys such as (value, uniqueSerial) while ordering primarily by value and returning only the value. With an appropriate balanced representation either approach preserves every occurrence and the required operation bounds. A bare claim about balanced search trees, with no treatment of duplicates, does not establish the abstract specification.Use one real linked list node per occurrence, with a private reference to the first:The representation invariant says that the reachable chain is finite, acyclic and nondecreasing in
class Node(val value: Int, var next: Node)
// Inside the queue implementation:
private var first: Node = nullvalue, and is owned by the queue. Equal adjacent values are explicitly allowed. The abstraction function is the multiset containing one occurrence of v.value for every reachable real linked list node v. Initially first==null represents the empty multiset. No artificial smallest Int is needed as a sentinel, and Int.MinValue remains a valid real value.Let The loop invariant is , every entry of is less than , every entry of is at least , and the segment's multiset is unchanged. Entries outside are unchanged. When
a be the given enclosing array. A two-way array partition needs only one scan, and does not require the pivot to occur in the array:def partition(l: Int, r: Int, p: Int): Int = {
require(0 <= l && l <= r && r <= a.length)
var k = l
var j = l
while (j < r) {
if (a(j) < p) {
val t = a(k)
a(k) = a(j)
a(j) = t
k += 1
}
j += 1
}
k
}a(j)<p, swapping it into position k extends the low part; if k<j, the displaced entry was in the high part and remains there after j advances. If k==j, the self-swap is harmless. The other branch simply extends the high part. The loop variant is . At exit , so the returned has the required separation property, including empty segments and arbitrarily many duplicate entries. The time complexity is and auxiliary space complexity is .A sentinel node supplies a permanent predecessor for the first real node. Inserting or deleting at the front then uses the same
predecessor.next update as an interior operation. Its datum is not a multiset element: the chosen extreme integer is merely unused sentinel data.A representation invariant requires a nonnull fixed header, a finite acyclic chain starting at
bagHeader.next, and exclusion of the header from that chain. Each node identity occurs once, although different nodes may have equal data. Representation links are owned by the implementation, so outside mutation cannot alter them unexpectedly.Check that at least one real node exists, then compose the two helpers:
def deleteMin(): Int = {
require(bagHeader.next != null, "bag is empty")
val smallest = findMin(bagHeader.next)
val answer = smallest.datum
delete(smallest)
answer
}The precondition check makes
findMin applicable. Its result belongs to the real-node sequence, satisfying delete's precondition. Consequently one minimum value is returned and exactly one occurrence is removed; duplicates remain as distinct nodes. Saving the datum before deletion also separates the return value from subsequent link mutation. Two linear traversals still give time for a multiset containing nodes, with constant auxiliary space.Let and describe the entry state. The postcondition is: the multiset is empty; the returned chain is sorted nondecreasingly; its node sequence is a permutation of . Thus it has exactly the original multiset of data and preserves all duplicates. Strict increase is impossible when the multiset contains repeated values.
def getAllSorted(): Node = {
var head: Node = null
var tail: Node = null
while (bagHeader.next != null) {
val nextSmallest = findMin(bagHeader.next)
delete(nextSmallest)
nextSmallest.next = null
if (head == null) head = nextSmallest
else tail.next = nextSmallest
tail = nextSmallest
}
head
}The loop invariant partitions the original node identities into the remaining multiset and the returned-prefix chain. The latter is sorted, its last node is
tail, and every datum in it is no larger than every remaining datum. Selecting a minimum and appending that detached node preserves all these facts. The loop variant is the number of real nodes remaining in the multiset, decreasing by one. When it reaches zero the string prefix contains the entire original node set, proving the postcondition. An initially empty multiset returns null.This is linked-list selection sort. Its time is in general, from successive scans of lengths , and it uses constant auxiliary space: it relinks original nodes instead of allocating a new node for each item.
The abstract data type invariant constrains a total count function on integer keys as follows:The initial state has every count zero, and . These conditions make the abstract state a multiset supported on the allowed finite key range. Since the interface returns an
Int, the implementations below use representable nonnegative multiplicities; equivalently add to the invariant, where . The corresponding add precondition below prevents overflow. An idealized unbounded-integer specification can omit that machine bound.Store the number of positive buckets, initially zero:Extend the representation invariant byThe abstraction function for the multiset is unchanged. The size operation has no precondition beyond a valid receiver; it returns the cardinality of the multiset support and changes no state. Replace the modifying methods inside the class byOnly transitions from zero to one or from one to zero change the support. Additions to an already positive bucket and removals leaving it positive preserve
private var distinct = 0
def size: Int = distinctdef add(x: Int): Unit = {
require(inRange(x))
require(c(x) < Int.MaxValue)
if (c(x) == 0) distinct += 1
c(x) += 1
}
def remove(x: Int): Unit = {
require(inRange(x))
require(c(x) > 0)
c(x) -= 1
if (c(x) == 0) distinct -= 1
}distinct. Thus size takes time, independently of MAX, and the other operations retain constant-time array access. Initialization still takes time.Apply the supplied bitonic-merging fact directly:For length ,
merge :: Ord a => [a] -> [a]
merge xs = rounds (levels (length xs)) xs
where
levels n
| n <= 1 = 0
| otherwise = 1 + levels (n `div` 2)
order (x,y)
| x <= y = (x,y)
| otherwise = (y,x)
pass = unpairs . map order . pairs . riffle
rounds 0 ys = ys
rounds r ys = rounds (r-1) (pass ys)levels n = k. Each pass performs exactly the required shuffle and adjacent compare-exchange operations. The supplied fact therefore proves that rounds k yields ascending order from a bitonic sequence. Each pass preserves the multiset of entries and costs , giving time for . Length one needs no pass. The definition also harmlessly returns [] on empty input.Nonnegative count functions form a valid subtype of the immutable value interface, but they are not closed under its exact subtraction. Preserve the inherited result type rather than silently changing the subtraction contract:
trait MultiSet[T] extends CompleteMultiSet[T] {
// Additional invariant: count(t) >= 0 for every t.
// Inherited arithmetic still returns CompleteMultiSet[T].
}
class MultiSetImp[T](initial: Map[T, Int])
extends CompleteMultiSetImp[T](initial) with MultiSet[T] {
require(initial.values.forall(_ >= 0), "negative multiplicity")
}
def ms[T](ts: T*): MultiSet[T] =
new MultiSetImp[T](ts.distinct.map(t => (t, 1)).toMap)The invariant guarantees nonnegative multiplicities in every
MultiSet object, and the factory gives each supplied element exactly one occurrence. Since the objects are immutable, inherited operations cannot invalidate that invariant in the receiver. Their declared result is a complete multiset, which can legitimately contain negative counts; for example, subtracting a positive count from zero produces a signed result.If an ordinary multiset-only difference is desired, introduce a separate operation with count , or an interface whose subtraction explicitly has that contract. Replacing the parent's exact difference by truncated difference under the same signature would violate behavioral subtyping; strengthening its precondition to forbid negative results would also fail to implement the parent's unrestricted mathematical contract. Retaining the complete result type above reuses every correct default without that substitution error.
Use a distinct constructor for each mark, and reserve every bull before allocating any cow:The multiset
data Mark = Bull | Cow | Miss deriving (Eq,Show)
scoreWordle :: Eq a => [a] -> [a] -> [Mark]
scoreWordle secret guess = mark pairs pool
where
pairs = zip secret guess
pool = [s | (s,g) <- pairs, s /= g]
mark [] _ = []
mark ((s,g):ps) available
| s == g = Bull : mark ps available
| otherwise = case pluck g available of
Nothing -> Miss : mark ps available
Just rest -> Cow : mark ps restpool contains exactly the unused secret tokens after all bulls have been set aside. The recursive invariant is that available contains those tokens not yet allocated to cows. A successful pluck consumes one, and failure leaves the pool unchanged. This proves both multiplicity correctness and the priority of bulls over cows. Among duplicate non-bull guess tokens, the leftmost eligible ones receive cows. For secret "alpha" and guess "aaabb", the result is [Bull,Cow,Miss,Miss,Miss]. This implements duplicate-aware positional scoring. Prime signature Created 2026-10-20 Updated 2026-10-25
The prime signature of is the multiset of positive prime exponents , conventionally sorted. For example, and both have signature . Quantities such as the divisor function and the number of ordered factorizations depend on this signature rather than the particular primes.
Product-sum number 2026-10-20
A positive integer that is both the sum and the product of one multiset of positive integers. For factors at least with product , sum , and count , appending ones gives without changing the product. For , the example with ones proves that the smallest product-sum value is at most .
Set Created 2026-09-24 Updated 2026-10-14
A set is a collection of distinct objects regarded as its set elements. Membership, written , is the basic question: is one of the objects in ? For example, : repeated entries and their written order do not change a set. A multiset retains multiplicities, while a tuple retains positions as well. Choosing the right object therefore determines which distinctions the mathematics remembers.
A set can collect possible outcomes of a probability space, vertices of a graph, or allowed inputs of a function. The empty set contains none; a subset selects members of a previously given set; the power set collects every such selection. These constructions are useful because they can be stated once and reused regardless of what the members represent. In an axiomatic account, rules such as the axiom schema of separation specify which collections exist as sets.
Signed multiset 2026-10-25
A finitely supported function , allowing negative as well as positive multiplicities. Missing keys denote zero. Addition, exact subtraction, pointwise maximum/minimum and pointwise order can be implemented over the union of operand supports. A nonnegative multiset is a refinement, but exact subtraction can leave that refinement while still producing a signed multiset. Fixed-width count implementations must also specify overflow behavior.
Sorting algorithm 2026-10-13
A sorting algorithm rearranges a finite sequence into nondecreasing order under a chosen total order on its keys. Its output must contain every input entry exactly once, including repetitions: producing ordered values is insufficient if entries are lost. Merge sort is one such algorithm; stable sorting adds a requirement about equal keys.
Sorting creates reusable structure: equal sort keys become adjacent, and a sorted array supports binary search. However, an algorithm correctness check must establish both sortedness and sorting permutation preservation. Replacing every entry by the smallest key can produce ordered keys while destroying the original data. Stable sorting imposes a third condition when equal sort keys occur: retain their arrival order.
For example, take complete sorting records , with the first component as the sort key and the letter as associated data. A correct output is . The sort keys are ordered, every complete sorting record remains, and the equal-key pairs keep before and before . The output satisfies sortedness and sorting permutation preservation, but fails stable sorting. The output has the right multiset of sort keys but loses and duplicates ; it fails sorting permutation preservation of complete sorting records.
To express stable sorting precisely, label every input occurrence by its original position, even when two complete sorting records have identical contents. Let be the output position of the occurrence originally at position . Besides sortedness and sorting permutation preservation, requireHere is the sort key function. Sorting by the pair under lexicographic order gives the same order as stable sorting by : the original position breaks ties without discarding associated data. For a two-field ordering, first apply stable sorting by the secondary sort key, then apply stable sorting by the primary sort key. The second pass retains the secondary order within each group of equal primary sort keys. For instance, first becomes by letter, then by number.
Sorting permutation preservation 2026-10-13
The output of a sorting algorithm must be a permutation of the complete input sequence. For equal lengths , require a permutation of such thatEquivalently, the input and output have the same multiset of complete entries: each entry has the same multiplicity. Equality of the underlying sets is insufficient; and have the same set of values and the same length but different multisets. When tracking occurrences rather than contents, retain original identity labels to check that each one is moved exactly once. A swap preserves this multiset automatically; an overwrite need not.