CSE 331�Software Design & Implementation
Spring 2026
Section 5 – Arrays
Administrivia
Array Notation
Other useful facts:
‘For Any’ Facts
A[i] < A[i+1] for any 0 ≤ i < len(A) – 1
// @requires A[i] < A[i+1] for any 0 <= i < len(A)-1
// @returns true if A[i] == y for any 0 <= i < len(A)
// false otherwise
public boolean bsearch(int[] A, int y) { … }
Array Mutation
{{ A[j] < A[j+1] for any 0 ≤ j ≤ 9 }}
A[0] = 100;
{{ (A[j] < A[j+1] for any 1 ≤ j ≤ 9) and A_0[0] < A[1] and A[0] = 100 }}
Mutating Arrays (add/remove)
{{ P(A) }} {{ P(A ⧺ [100]) }}
A.add(100); A.add(100);
{{ P(A_0) and A = A_0 ⧺ [100] }} {{ P(A) }}
{{ P(A) }} {{ P(A[ : len(A) - 1]) }}
A.remove(A.size()-1); A.remove(A.size()-1);
{{ P(A_0) and {{ P(A) }}
A = A_0[ : len(A_0) – 1] }}
Task 1 – Tw-Y’s Removed
Considering the following method specification:
/**
* Removes all instances of an int from the provided array, writing
* the remaining elements into the first j indicies and returning j.
* @param y the int to remove
* @param arr the array to remove from
* @requires arr != null
* @modifies arr
* @effects arr[: j] holds all the non-y elements in arr
* @returns j the length of the non-y elements in arr
*/
public static int remove(int[] arr, int y);
In this problem, we will check the correctness of the following code that implements remove.
Task 1 – Tw-Y’s Removed
public static int remove(int[] arr, int y) {
int i = 0;
int j = 0;
{{ P1: ______________________________________________________________ }}
{{ Inv: arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0 }}
Task 1 – Tw-Y’s Removed
public static int remove(int[] arr, int y) {
int i = 0;
int j = 0;
{{ P1: i = 0 and j = 0 (and arr = arr_0, optionally) }}
{{ Inv: arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0 }}
Task 1 – Tw-Y’s Removed
public static int remove(int[] arr, int y) {
int i = 0;
int j = 0;
{{ P1: i = 0 and j = 0 (and arr = arr_0, optionally) }}
{{ Inv: arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0 }}
The first fact of Inv holds since:
arr[: j] = arr[: 0] since j = 0
= nil
= arr_0[: 0]
= arr_0[: i] since i = 0
Since j is 0, arr[: j] is the empty array. The same holds for i = 0, so arr_0[: i] is also the empty array. There cannot be any non-y elements in the empty array, so this fact clearly holds.
Task 1 – Tw-Y’s Removed
public static int remove(int[] arr, int y) {
int i = 0;
int j = 0;
{{ P1: i = 0 and j = 0 (and arr = arr_0, optionally) }}
{{ Inv: arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0 }}
The second fact of Inv holds since:
arr[i :] = arr[0 :] since i = 0
= arr
= arr_0 since arr = arr_0
= arr_0[0 :]
= arr_0[i :] since i = 0
Since i is 0, arr_0[i :] = arr_0. Since arr has not been modified, arr = arr_0. We know that arr_0 = arr_0[0 :], and so arr_0[i :] = arr[i :]
Task 1 – Tw-Y’s Removed
public static int remove(int[] arr, int y) {
int i = 0;
int j = 0;
{{ P1: i = 0 and j = 0 (and arr = arr_0, optionally) }}
{{ Inv: arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0 }}
The third fact of Inv holds since:
length of arr = length of arr_0 since arr = arr_0
Since arr has not been reassigned, the length of arr = length of arr_0.
Task 1 – Tw-Y’s Removed
b) Fill in P4 using forward reasoning and then prove that P4 implies the postcondition.
{{ Inv: arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0 }}
while (i != arr.length) {
...
}
{{ P4: ______________________________________________________________ }}
{{ Post: arr[: j] contains all non-y elements in arr_0 }}
return j;
Task 1 – Tw-Y’s Removed
b) Fill in P4 using forward reasoning and then prove that P4 implies the postcondition.
{{ Inv: arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0 }}
while (i != arr.length) {
...
}
{{ P4: Inv (arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0) and
i = arr.length }}
{{ Post: arr[: j] contains all non-y elements in arr_0 }}
return j;
Task 1 – Tw-Y’s Removed
b) Fill in P4 using forward reasoning and then prove that P4 implies the postcondition.
{{ Inv: arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0 }}
while (i != arr.length) {
...
}
{{ P4: Inv (arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0) and
i = arr.length }}
{{ Post: arr[: j] contains all non-y elements in arr_0 }}
return j;
This gives us the postcondition as:
Since i = arr.length and arr.length = arr_0.length, i = arr_0.length. Thus, arr_0[: i] = arr_0[: arr_0.length] = arr_0. Since we have that arr[: j] contains all non-y elements from arr_0[: i], we see that arr[: j] contains all non-y elements from arr_0.
Task 1 – Tw-Y’s Removed
c) Fill in P2 using forward reasoning and Q2 using backward. Then, prove that P2 implies Q2.
{{ Inv: arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0 }}
while (i != arr.length) {
if (arr[i] == y) {
{{ P2: ______________________________________________________ }}
{{ Q2: ______________________________________________________ }}
} else {
...
}
i = i + 1;
}
Task 1 – Tw-Y’s Removed
c) Fill in P2 using forward reasoning and Q2 using backward. Then, prove that P2 implies Q2.
{{ Inv: arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0 }}
while (i != arr.length) {
if (arr[i] == y) {
{{ P2: Inv (arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0) and
i != arr.length, and arr[i] = y }}
{{ Q2: ______________________________________________________ }}
} else {
...
}
i = i + 1;
}
Task 1 – Tw-Y’s Removed
c) Fill in P2 using forward reasoning and Q2 using backward. Then, prove that P2 implies Q2.
{{ Inv: arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0 }}
while (i != arr.length) {
if (arr[i] == y) {
{{ P2: Inv (arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0) and
i != arr.length, and arr[i] = y }}
{{ Q2: arr[: j] contains all non-y elements from arr_0[: i+1] and
arr[i+1 :] = arr_0[i+1 :] and length of arr = length of arr_0}}
} else {
...
}
i = i + 1;
}
Task 1 – Tw-Y’s Removed
c) Fill in P2 using forward reasoning and Q2 using backward. Then, prove that P2 implies Q2.
{{ P2: Inv (arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0) and
i != arr.length, and arr[i] = y }}
{{ Q2: arr[: j] contains all non-y elements from arr_0[: i+1] and
arr[i+1 :] = arr_0[i+1 :] and length of arr = length of arr_0}}
The first part follows since:
Since arr[i :] = arr_0[i :], arr[i] = arr_0[i]. This gives us arr[i] = arr_0[i] = y, meaning arr_0[i] is not a non-y element. Since arr[: j] contains all non-y elements in arr_0[: i] and arr_0[i] = y, we also know that arr[: j] contains all non-y elements from arr_0[: i+1].
The second part of Q2 is implied by the second fact from P2 (since arr[i+1 :] is a sublist of arr[i :] and the same for arr_0).
The third fact is directly implied.
Task 1 – Tw-Y’s Removed
d) Fill in P3 using forward reasoning and Q3 using backward. Then, prove that P3 implies Q3.
{{ Inv: arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0 }}
while (i != arr.length) {
if (arr[i] == y) {
...
} else {
{{ P3: ______________________________________________________ }}
{{ Q3: ______________________________________________________ }}
arr[j] = arr[i];
j = j + 1;
}
i = i + 1;
}
Task 1 – Tw-Y’s Removed
d) Fill in P3 using forward reasoning and Q3 using backward. Then, prove that P3 implies Q3.
{{ Inv: arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0 }}
while (i != arr.length) {
if (arr[i] == y) {
...
} else {
{{ P3: Inv (arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0), and
i != len(arr) and arr[i] != y }}
{{ Q3: ______________________________________________________ }}
arr[j] = arr[i];
j = j + 1;
}
i = i + 1;
}
Task 1 – Tw-Y’s Removed
d) Fill in P3 using forward reasoning and Q3 using backward. Then, prove that P3 implies Q3.
{{ Inv: arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0 }}
while (i != arr.length) {
if (arr[i] == y) {
...
} else {
{{ P3: Inv (arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0), and
i != len(arr) and arr[i] != y }}
{{ Q3: (arr[: j] ++ arr[i] contains all non-y elements from arr_0[: i+1] and
arr[i+1 :] = arr_0[i+1 :] and length of arr = length of arr_0) }}
arr[j] = arr[i];
j = j + 1;
}
i = i + 1;
}
Task 1 – Tw-Y’s Removed
d) Fill in P3 using forward reasoning and Q3 using backward. Then, prove that P3 implies Q3.
{{ P3: Inv (arr[: j] contains all non-y elements from arr_0[: i] and
arr[i :] = arr_0[i :] and length of arr = length of arr_0), and
i != len(arr) and arr[i] != y }}
{{ Q3: (arr[: j] ++ arr[i] contains all non-y elements from arr_0[: i+1] and
arr[i+1 :] = arr_0[i+1 :] and length of arr = length of arr_0) }}
The first fact follows since:
Since arr[i :] = arr_0[i :], arr[i] = arr_0[i]. Thus, arr[i] = arr_0[i] != y and arr[i] must be included in the non-y elements for arr_0[: i+1]. Since arr[: j] contains all non-y elements from arr_0[: i], concatenating arr[: j] ++ arr[i] would also contains all non-y elements from arr_0[: i + 1]. Thus, fact 1 holds.
The second part of Q3 is implied by the second fact from P3 (since arr[i+1 :] is a sublist of arr[i :]).
The third fact is directly implied.
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:
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_0[i : ]
while (________________________________________) {
}
Fill in the missing parts of the code, to make it correct with the given invariant:
Task 2 – Rally the Loops
int i = 0;
// Inv: A[0 : i] = A_0[0 : i] with every y replaced with a z
// and A[i : ] = A_0[i : ]
while ( i < A.length ) {
}
Fill in the missing parts of the code, to make it correct with the given invariant:
Task 2 – Rally the Loops
int i = 0;
// Inv: A[0 : i] = A_0[0 : i] with every y replaced with a z
// and A[i : ] = A_0[i : ]
while ( i < A.length ) {
if (A[i] == y) {
A[i] = z;
}
i++;
}
Fill in the missing parts of the code, to make it correct with the given invariant:
Task 2 – Rally the Loops
int i = ____________________
// Inv: A[0 : i] = A_0[0 : i] and A[i : ] = A_0[i : ] with
// every y replaced with a z
while (________________________________________) {
}
Fill in the missing parts of the code, to make it correct with the given invariant:
Task 2 – Rally the Loops
int i = A.length;
// Inv: A[0 : i] = A_0[0 : i] and A[i : ] = A_0[i : ] with
// every y replaced with a z
while ( i > 0 ) {
}
Fill in the missing parts of the code, to make it correct with the given invariant:
Task 2 – Rally the Loops
int i = A.length;
// Inv: A[0 : i] = A_0[0 : i] and A[i : ] = A_0[i : ] with
// every y replaced with a z
while ( i > 0 ) {
i--;
if (A[i] == y) {
A[i] = z;
}
}
Fill in the missing parts of the code, to make it correct with the given invariant:
Testing - Review
• Statement Coverage
• Branch Coverage
• Loop Coverage
• Exhaustive Testing
Mutable Testing
Task 3 – Test, Ice, Compression, Elevation
/**
* Join the two given lists into a single one
* @requires first != null, second != null
* @returns first concatenated with second
*/
public static List<Integer> join(List<Integer> first,
List<Integer> second) {
List<Integer> newList = new ArrayList<>();
newList.addAll(first);
newList.addAll(second);
return newList;
}
a) First, consider a version of join, which does not mutate either argument:
Task 3 – Test, Ice, Compression, Elevation
public static List<Integer> join(List<Integer> first,
List<Integer> second) {
List<Integer> newList = new ArrayList<>();
newList.addAll(first);
newList.addAll(second);
return newList;
}
Fill in the missing parts of the JUnit test for join:
@Test
public void testJoin(){
List<Integer> list1 = new ArrayList<>(Arrays.asList(1, 2));
List<Integer> list2 = new ArrayList<>(Arrays.asList(3, 4));
assertEquals(________________________, join(list1, list2));
List<Integer> list3 = new ArrayList<>(Arrays.asList(1));
List<Integer> list4 = new ArrayList<>(Arrays.asList(2, 3, 4));
assertEquals(________________________, join(list3, list4));
}
Task 3 – Test, Ice, Compression, Elevation
public static List<Integer> join(List<Integer> first,
List<Integer> second) {
List<Integer> newList = new ArrayList<>();
newList.addAll(first);
newList.addAll(second);
return newList;
}
Fill in the missing parts of the JUnit test for join:
@Test
public void testJoin(){
List<Integer> list1 = new ArrayList<>(Arrays.asList(1, 2));
List<Integer> list2 = new ArrayList<>(Arrays.asList(3, 4));
assertEquals(Arrays.asList({1, 2, 3, 4}), join(list1, list2));
List<Integer> list3 = new ArrayList<>(Arrays.asList(1));
List<Integer> list4 = new ArrayList<>(Arrays.asList(2, 3, 4));
assertEquals(________________________, join(list3, list4));
}
Task 3 – Test, Ice, Compression, Elevation
public static List<Integer> join(List<Integer> first,
List<Integer> second) {
List<Integer> newList = new ArrayList<>();
newList.addAll(first);
newList.addAll(second);
return newList;
}
Fill in the missing parts of the JUnit test for join:
@Test
public void testJoin(){
List<Integer> list1 = new ArrayList<>(Arrays.asList(1, 2));
List<Integer> list2 = new ArrayList<>(Arrays.asList(3, 4));
assertEquals(Arrays.asList({1, 2, 3, 4}), join(list1, list2));
List<Integer> list3 = new ArrayList<>(Arrays.asList(1));
List<Integer> list4 = new ArrayList<>(Arrays.asList(2, 3, 4));
assertEquals(Arrays.asList({1, 2, 3, 4}), join(list3, list4));
}
Task 3 – Test, Ice, Compression, Elevation
/** Join the two given lists into a single one
* @requires first != null, second != null
* @requires first != second
* @modifies first
* @effects first is first_0 concatenated with second
*/
public static void join(List<Integer> first,
List<Integer> second) {
first.addAll(second);
}
b) Next, consider the following version of join, which mutates first and does not return anything
Rewrite the JUnit test to use this new definition of join on the same inputs as above.
Task 3 – Test, Ice, Compression, Elevation
@Test
public void testJoin(){
}
Task 3 – Test, Ice, Compression, Elevation
@Test
public void testJoin(){
List<Integer> list1 = new ArrayList<>(Arrays.asList(1, 2));
List<Integer> list2 = new ArrayList<>(Arrays.asList(3, 4));
join(list1, list2);
assertEquals(Arrays.asList(1, 2, 3, 4), list1);
}
Task 3 – Test, Ice, Compression, Elevation
@Test
public void testJoin(){
List<Integer> list1 = new ArrayList<>(Arrays.asList(1, 2));
List<Integer> list2 = new ArrayList<>(Arrays.asList(3, 4));
join(list1, list2);
assertEquals(Arrays.asList(1, 2, 3, 4), list1);
List<Integer> list3 = new ArrayList<>(Arrays.asList(1));
List<Integer> list4 = new ArrayList<>(Arrays.asList(2, 3, 4));
join(list3, list4);
assertEquals(Arrays.asList(1, 2, 3, 4), list3);
}