1 of 31

CSE 331�Software Design & Implementation

​

Summer 2026

Section 4 – Loop Invariants & ADTs

2 of 31

Specifications for ADTs – Review

  • Terminology for specifying ADTs:
  • Abstract State / Representation (Math)
    • How clients should understand the object
    • Ex: List(nil or cons)

​

  • Concrete State / Representation (Code)
    • Actual fields of the record and the data stored
    • Ex:

​

public class List {

final int hd;

final List tl;

}

3 of 31

State Representations

  • We’ve had different abstract and concrete types all along!
    • in our math, List is an inductive type (abstract)
    • in our code, List is a class with two fields (concrete)

​

​

  • Term “this” will refer to abstract state
    • this is the mathematical value that the record represents (similar to a specific instance of a class)

​

4 of 31

Internally Documenting ADTs – Review

Abstract Function (AF) – defines what abstract state the field values represent

    • Maps field values → the object they represent
    • Output is math, this is a mathematical function

​

Representation Invariants (RI) – facts about the field values that must always be true

    • Constructor must always make sure RI is true at runtime
    • Can assume RI is true when reasoning about methods
    • AF only needs to make sense when RI holds
    • Must ensure that RI always holds

5 of 31

Documenting ADTs – Example

// A list of integers that can retrieve the last element in O(1)

interface FastList {

/**

* Returns the object as a regular list

* @returns this

*/

List toList();

}

class FastLastList implements FastList {

// RI: this.last = last(this.list);

// AF: this = this.list;

// @returns last(this)

int getLast() {

return this.last;

};

}

Hide the representation details (i.e. real fields) from the client

Talk about functions in terms of the abstract state (this)

6 of 31

Externally Documenting ADTs - Review

  • For mutable ADTs, will have 2 additional tags to describe “mutator” methods

* @modifies states what could be mutated by function (this)

* @effects Detailed description of guaranteed changes

  • JavaDoc comments in interfaces use “tags” to describe what ADT methods do in terms of the abstract state

/**

* High level description of what function does

* @param a What "a" represents + any conditions

* @requires Rules about multiple params and Abstract State (this)

* @returns Detailed description of return value

* @throws Condition when errors will be thrown

*/

7 of 31

Loop Invariant – Review

  • Loop invariant must be true every time at the top of the loop
    • The first time (before any iterations) and for the beginning of each iteration
  • Also true every time at the bottom of the loop
    • Meaning it’s true immediately after the loop exits
  • During the body of the loop (during S), it isn’t true

​

  • Must use “Inv” notation to indicate that it’s not a standard assertion

true!

{{Inv: I}}

while (cond) {

S

}

true!

true!

true!

8 of 31

Question ….

Where is it allowed for a loop invariant not to hold?

​

  • before the loop

​

  • after the loop

​

  • after entering the loop

​

  • before exiting the loop

​

  • during the code execution inside of the loop

9 of 31

Question ….

Where is it allowed for a loop invariant not to hold?

​

  • before the loop

​

  • after the loop

​

  • after entering the loop

​

  • before exiting the loop

​

  • during the code execution inside of the loop

10 of 31

Task 1 - Everybody Loops

Goal:

Show that our loop correctly computes the quotient of x / 10.

I.e., that the loop correctly finds the largest integer y such that 10y ≤ x.

​

​

10(y+1) is TOO LARGE.

11 of 31

Task 1 - Everybody Loops

// Computes the integer quotient of x divided by 10

// @param x The numerator

// @requires x >= 0

// @return The largest integer y such that 10 * y <= x_0

public static int divideByTen(int x) {

{{ x = x_0 and x_0 >= 0 }}

int y = 0;

{{ P1: ______________________________ }}

{{ Inv: x_0 - 10y = x and x >= 0 }}

while (x >= 10) {

{{ ____________________________________ }}

y = y + 1;

{{ __________________________________________ }}

x = x - 10;

{{ P3: _________________________________________________________ }}

{{ Q2: _____________________________ }}

}

{{ P2: _________________________________________ }}

{{ Q1: 10y <= x_0 and x_0 < 10(y+1) and x = x_0 - 10y }}

return y;

}

​

Task a)

Fill in P1. Then, show that the invariant is true when we get to the top of the loop for the first time.

​

i.e., show that P1 implies the loop invariant

12 of 31

Task 1 - Everybody Loops

// Computes the integer quotient of x divided by 10

// @param x The numerator

// @requires x >= 0

// @return The largest integer y such that 10 * y <= x_0

public static int divideByTen(int x) {

{{ x = x_0 and x_0 >= 0 }}

int y = 0;

{{ P1: ______________________________ }}

{{ Inv: x_0 - 10y = x and x >= 0 }}

while (x >= 10) {

{{ ____________________________________ }}

y = y + 1;

{{ __________________________________________ }}

x = x - 10;

{{ P3: _________________________________________________________ }}

{{ Q2: _____________________________ }}

}

{{ P2: _________________________________________ }}

{{ Q1: 10y <= x_0 and x_0 < 10(y+1) and x = x_0 - 10y }}

return y;

}

​

Task b)

Fill in P2. Then, show that Q1 holds when we exit the loop

​

i.e., show that P1 implies the loop invariant

13 of 31

Task 1 - Everybody Loops

// Computes the integer quotient of x divided by 10

// @param x The numerator

// @requires x >= 0

// @return The largest integer y such that 10 * y <= x_0

public static int divideByTen(int x) {

{{ x = x_0 and x_0 >= 0 }}

int y = 0;

{{ P1: ______________________________ }}

{{ Inv: x_0 - 10y = x and x >= 0 }}

while (x >= 10) {

{{ ____________________________________ }}

y = y + 1;

{{ __________________________________________ }}

x = x - 10;

{{ P3: _________________________________________________________ }}

{{ Q2: _____________________________ }}

}

{{ P2: _________________________________________ }}

{{ Q1: 10y <= x_0 and x_0 < 10(y+1) and x = x_0 - 10y }}

return y;

}

​

Task c)

Fill in Q2. Then, forward reason to P3. Show that P3 implies Q2, proving that the body of the loop is correct.

​

​

Hint: What do we know at the end of a loop?

14 of 31

Task 1 - Everybody Loops

// Computes the integer quotient of x divided by 10

// @param x The numerator

// @requires x >= 0

// @return The largest integer y such that 10 * y <= x_0

public static int divideByTen(int x) {

{{ x = x_0 and x_0 >= 0 }}

int y = 0;

{{ P1: ______________________________ }}

{{ Inv: x_0 - 10y = x and x >= 0 }}

while (x >= 10) {

{{ ____________________________________ }}

y = y + 1;

{{ __________________________________________ }}

x = x - 10;

{{ P3: _________________________________________________________ }}

{{ Q2: _____________________________ }}

}

{{ P2: _________________________________________ }}

{{ Q1: 10y <= x_0 and x_0 < 10(y+1) and x = x_0 - 10y }}

return y;

}

​

Task c)

Fill in Q2. Then, forward reason to P3. Show that P3 implies Q2, proving that the body of the loop is correct.

​

​

15 of 31

Task 1 - Everybody Loops

// Computes the integer quotient of x divided by 10

// @param x The numerator

// @requires x >= 0

// @return The largest integer y such that 10 * y <= x_0

public static int divideByTen(int x) {

{{ x = x_0 and x_0 >= 0 }}

int y = 0;

{{ P1: ______________________________ }}

{{ Inv: x_0 - 10y = x and x >= 0 }}

while (x >= 10) {

{{ ____________________________________ }}

y = y + 1;

{{ __________________________________________ }}

x = x - 10;

{{ P3: _________________________________________________________ }}

{{ Q2: _____________________________ }}

}

{{ P2: _________________________________________ }}

{{ Q1: 10y <= x_0 and x_0 < 10(y+1) and x = x_0 - 10y }}

return y;

}

​

Task c)

Fill in Q2. Then, forward reason to P3. Show that P3 implies Q2, proving that the body of the loop is correct.

​

​

16 of 31

Task 1 - Everybody Loops

// Computes the integer quotient of x divided by 10

// @param x The numerator

// @requires x >= 0

// @return The largest integer y such that 10 * y <= x_0

public static int divideByTen(int x) {

{{ x = x_0 and x_0 >= 0 }}

int y = 0;

{{ P1: ______________________________ }}

{{ Inv: x_0 - 10y = x and x >= 0 }}

while (x >= 10) {

{{ ____________________________________ }}

y = y + 1;

{{ __________________________________________ }}

x = x - 10;

{{ P3: _________________________________________________________ }}

{{ Q2: _____________________________ }}

}

{{ P2: _________________________________________ }}

{{ Q1: 10y <= x_0 and x_0 < 10(y+1) and x = x_0 - 10y }}

return y;

}

​

Task c)

Fill in Q2. Then, forward reason to P3. Show that P3 implies Q2, proving that the body of the loop is correct.

​

​

17 of 31

Array Notation (Review)

  • We use the notation a[i..j] to refer to the elements of an array a from index i (inclusive) to j (exclusive). So a[0..n] is the elements of a at indices 0, 1, ..., n-1.

​

Other useful facts:

    • A[A.length .. j] = null
    • A[i .. 0] = null
    • A[0 .. i] ⧺ A[i .. A.length]*
    • A[0 .. A.length] = A

​

​

​

​

​

*The “⧺” symbol represents concatenation between two lists.

18 of 31

Task 2 – Rally the Loops

/**

* Writes over each copy of y in A with the value z.

* @param A the array to replace values in

* @param y the value to be replaced in A

* @param z the value to replace y with in A

* @modifies A

* @effects A = A_0 with every instance of y replaced

* with a z

*/

public void replace(int[] A, int y, int z) { .. }

In this problem, we’ll be implementing the following function:

19 of 31

Task 2 – Rally the Loops

int i = ____________________

​

// Inv: A[0 .. i] = A_0[0 .. i] with every y replaced with a z

// and A[i .. A.length] = A_0[i .. A.length]

while (________________________________________) {

​

​

​

​

​

​

​

}

Fill in the missing parts of the code, to make it correct with the given invariant:

20 of 31

Task 2 – Rally the Loops

int i = ____________________

​

// Inv: A[0 .. i] = A_0[0 .. i] and A[i .. A.length] =

// A_0[i .. A.length] with every y replaced with a z

while (________________________________________) {

​

​

​

​

​

​

​

}

Fill in the missing parts of the code, to make it correct with the given invariant:

21 of 31

Task 3 - Rich AF

Consider three different concrete representations for MutableIntSet:

public class MutableIntSetImpl implements MutableIntSet {

​

(1) // AF: this = this.elems[0 .. size]

private int[] elems;

private int size;

​

(2) // AF: this = this.elems[0 .. size]

// RI: this.elems contains no dups

private int[] elems;

private int size;

​

(3) // AF: this = this.elems[0 .. size]

// RI: this.elems is sorted

private int[] elems;

private int size;

​

public MutableIntSetImpl(int[] elems) {

this.elems = elems;

this.size = elems.length;

}

22 of 31

Task 3 - Rich AF

  1. State the concrete representations (1--3) for which it would satisfy the specification of the method in MutableIntSet. Why?

​

public boolean contains(int n) {

return Arrays.binarySearch(this.elems, n) >= 0;

}

23 of 31

Task 3 - Rich AF

b) State the concrete representations (1--3) for which it would satisfy the specification of the method in MutableIntSet. Why?

public boolean contains(int n) {

for (int i = 0; i < this.elems.length; i++) {

if (this.elems[i] == n)

return true;

}

return false;

}

24 of 31

Task 3 - Rich AF

c) State the concrete representations (1--3) for which it would satisfy the specification of the method in MutableIntSet. Why?

public void add(int n) {

if (!this.contains(n)) {

if (size >= this.elems.length) {

int[] temp = new int[size * 2 + 1];

for (int i = 0; i < this.elems.length; i++) {

temp[i] = this.elems[i];

}

this.elems = temp;

}

this.elems[size++] = n;

}

}

25 of 31

Task 3 - Rich AF

d) State the concrete representations (1--3) for which it would satisfy the specification of the method in MutableIntSet. Why?

public boolean remove(int n) {

for (int i = 0; i < this.elems.length; i++) {

if (this.elems[i] == n) {

size--;

for (int j = i; j < size - 1; j++) {

this.elems[j] = this.elems[j + 1];

}

return true;

}

}

return false;

}

26 of 31

MutableIntSet ADT

/**

* Represents a **mutable** integer set, or a collection of distinct integers.

*/

public class MutableIntSet {

/**

* Determines whether n is in the set.

* @param n the number to look for in the set

* @return true if n is in the set, false otherwise

*/

public boolean contains(int n);

​

/**

* Adds n to the set if not already present.

* @param n the number to add to the new set.

* @modifies this

* @effects this is unchanged if this_0 contains n

* otherwise, this contains all of this_0 and n

*/

public void add(int n);

​

/**

* Removes the desired int from the set.

* @param n The int to remove

* .... To complete in part d

*/

public boolean remove(int n);

}

​

27 of 31

Task 4 - Good News and Add News

  1. Would T.add(3) actually change this? If not, why is that allowed when it says @modifies this?

MutableIntSet T = {1, 2, 3}

28 of 31

Task 4 - Good News and Add News

b) Consider the following static method:

/**

* Adds n to the set if not already present.

* @param old the set to add to

* @param n the number to add to the new set.

* @requires old is not null

* @return a set with n and all of the elements of old.

* If old.contains(n), the new set has all the same elements as 'old'.

*/

public static MutableIntSet add(MutableIntSet old, int n);

Now, consider a call T.add(4). Explain how the operation of MutableIntSet.add differs from that of a call to static add(T, 4) in terms of this.

​

29 of 31

Task 4 - Good News and Add News

c) What is the abstract state of 𝑇 after the following code (This is forward reasoning.):

T.add(4);

T.add(2);

T.add(0);

​

MutableIntSet T = {1, 2, 3}

30 of 31

Task 4 - Good News and Add News

d) Write a specification for the method remove. You should have two cases - n is in the set, and n is not. Clearly explain how the abstract state changes after the method call and what is returned.

​

31 of 31

Fin

Remember to submit your half-sheets!