CSE 331: Software Design and Implementation, Autumn 2026
Your job is to deliver code that you know works.
6/**
7 * Converts a duration in hours, minutes, and seconds to seconds.
8 * The result could overflow an int, so it is returned as a long.
9 *
10 * @param h the hours
11 * @param m the minutes
12 * @param s the seconds
13 * @requires 0 <= h, 0 <= m, and 0 <= s
14 * @return 3600 * h + 60 * m + s
15 */16static long seconds(int h, int m, int s) {
17 long t = h;
18 t = t * 60 + m;
19 t = t * 60 + s;
20 return t;
21}return: no if, no loop6@Test
7void allZero() {
8 assertEquals(0, Seconds.seconds(0, 0, 0));
9}
10
11@Test
12void oneOfEach() {
13 assertEquals(3661, Seconds.seconds(1, 1, 1));
14}
15
16@Test
17void ninetyMinutes() {
18 assertEquals(5400, Seconds.seconds(0, 90, 0));
19}if and no loop, so every test runs every lineh, m, and sSum in prose; today we make it precise16static long seconds(int h, int m, int s) {
{{ 0 <= h and 0 <= m and 0 <= s }}
17 long t = h;
{{ 0 <= h and 0 <= m and 0 <= s and t = h }}
nothing about h, m, s changed, and now t = h too
18 t = t * 60 + m;
{{ 0 <= h and 0 <= m and 0 <= s and t = 60 * h + m }}
t = h is no longer true: the new t is the old one, h, times 60, plus m
19 t = t * 60 + s;
{{ 0 <= h and 0 <= m and 0 <= s and
t = 3600 * h + 60 * m + s }}
the same with s: the old t was 60 * h + m
20 return t;* and + as the operations on the integers, ℤint wraps around at the top of its range (Lecture 1)seconds returns a long, and its inputs are int valueslong's topint, with the clause its specification needs16static int abs(int n) {
17 if (n < 0) {
18 n = -n;
19 }
20 return n;
21}Only the runs with n < 0 reach line 18: a claim there is about those runs
Every run reaches line 20, once: a claim there is about every run
{{ P }} code {{ Q }}: a claim, code, and a claimseconds showed its triple valid{{ true }} x = 5; {{ x = 5 }}x 5: valid{{ x = 3 }} y = x + 1; {{ y = 4 }}x is 3, the line makes y 4: valid{{ x = 3 }} y = x + 1; {{ 0 < y }}y 4, and 4 is positive: valid too{{ true }} y = x + 1; {{ y = 4 }}x may be 0, and then y is 1: a counterexample, not valid{{ 0 < x }} x = x - 1; {{ 0 < x }}x may be 1, and then x is 0: not valid{{ false }} x = 0; {{ x = 1 }}false true, so nothing is asked: valid{{ P }}
{{ Q }}seconds argument was an empty triple{{ 0 <= h and 0 <= m and 0 <= s and t = h }}
{{ 0 <= h and t = h }}
{{ 0 <= t }}t = h, 0 <= t, truetrue; the strongest is false0 <= t: it holds, but says lesst is in terms of h, m, s{{ P }}
code
{{ ? }}{{ ? }}
code
{{ Q }}{{ P }} code {{ Q }}:{{ P }} code {{ R }} is valid because we found R that way{{ R }} code {{ Q }} is valid because we found R that wayifseconds before return{{ P }}
x = e;
{{ P[x ↦ x0] and x = e[x ↦ x0] }}x is any variable; e any expression that changes no variableP[x ↦ x0] is P with every x replaced by x0x had before the line{{ t = h }}
t = t * 60 + m;
{{ t0 = h and t = t0 * 60 + m }}
t = t * 60 + s;
{{ t0 = h and t1 = t0 * 60 + m and t = t1 * 60 + s }}t = h, the one fact after line 17 that mentions tt again, and t0 is takent1{{ t = h }}
t = t * 60 + m;
{{ t0 = h and t = t0 * 60 + m }}
t = t * 60 + s;
{{ t0 = h and t1 = t0 * 60 + m and t = t1 * 60 + s }}
{{ t1 = h * 60 + m and t = t1 * 60 + s }}
{{ t = (h * 60 + m) * 60 + s }}
{{ t = 3600 * h + 60 * m + s }}e never mention x, nothing has to be renamedP and x = e: line 17x without x0x0 in every other factx0 says nothing of the state: drop ith for t0; after x = x + 1, the old x is x - 1w renames too: w = x + 1 becomes w = x0 + 1{{ Q[x ↦ e] }}
x = e;
{{ Q }}x is any variable; e any expression that changes no variableQ[x ↦ e] is Q with every x replaced by ee is what x is about to becomex after had better be true of e before {{ 0 <= h and 0 <= m and 0 <= s }}
{{ true }}
{{ 3600 * h + 60 * m + s = 3600 * h + 60 * m + s }}
{{ (h * 60 + m) * 60 + s = 3600 * h + 60 * m + s }}
17 long t = h;
{{ (t * 60 + m) * 60 + s = 3600 * h + 60 * m + s }}
18 t = t * 60 + m;
{{ t * 60 + s = 3600 * h + 60 * m + s }}
19 t = t * 60 + s;
{{ t = 3600 * h + 60 * m + s }}17 long t = h;
{{ 0 <= h and 0 <= m and 0 <= s and t = h }}
{{ (t * 60 + m) * 60 + s = 3600 * h + 60 * m + s }}
18 t = t * 60 + m;(t * 60 + m) * 60 + s = (h * 60 + m) * 60 + s since t = h
= 3600 * h + 60 * m + st = h{{ ? }} x = x + 1; {{ x = 4 }}{{ P }}
first
{{ R }}
second
{{ Q }}seconds used this rule twiceseconds: the rules gave the claim after line 19{{ P }}
{{ A }}
code
{{ B }}
{{ Q }}{{ A }} code {{ B }} is valid, and B implies Q{{ 0 <= h and 0 <= m and 0 <= s and
t = 3600 * h + 60 * m + s }}
{{ t = 3600 * h + 60 * m + s }}
return t;t = hseconds arguments were this rule0 <= t after line 18: it broke no rule9/**
10 * Gives the absolute value of a number.
11 *
12 * @param n the number
13 * @requires -2147483647 <= n
14 * @return n if 0 <= n, and -n if n < 0
15 */int, -2147483648; why at the end16static int abs(int n) {
17 if (n < 0) {
18 n = -n;
19 }
20 return n;
21}n < 0, and n = -n makes n positive0 <= nif: 0 < n or 0 <= n, which is 0 <= n{{ P }}
if (b) {
{{ P and b }}
first
{{ A }}
} else {
{{ P and not b }}
second
{{ B }}
}
{{ A or B }}
{{ Q }}b is the if's condition; A and B the strongest claims after each branchb true; the false, b falseif, a run has taken one branch or the otherA or Bif with no else still has a false branch (Lecture 5)P and not bn: the claims need a name for the number givenn0 be the value of n at the calln, it means n0n = -n from wrapping: no claim needs moren = n0: one value for the whole argument16static int abs(int n) {
{{ n = n0 }}
17 if (n < 0) {
{{ n = n0 and n < 0 }}
18 n = -n;
{{ n0 < 0 and n = -n0 }}
19 }
{{ (n0 < 0 and n = -n0) or (0 <= n0 and n = n0) }}
20 return n;
21}n0 < 0 and n = -n0; the specification promises -n00 <= n0 and n = n0; it promises n0abs satisfies its specification6/**
7 * Compares two numbers.
8 *
9 * @param a one number
10 * @param b the other number
11 * @return -1 if a < b, 0 if a == b, and 1 if b < a
12 */ifs, and the body of each is a returnreturn ends the run: no run comes out of line 15 to after the ifP and not bfalse among them: no run reaches itfalse, no run reaches itreturn, check the postcondition's case13static int compare(int a, int b) {
{{ true }}
14 if (a < b) {
{{ a < b }}
15 return -1;
16 }
{{ b <= a }}
17 if (a == b) {
{{ b <= a and a = b }}
18 return 0;
19 }
{{ b <= a and a != b }}
20 return 1;
21}a < b, and the specification promises -1a = b, and it promises 0b <= a and a != b say b < a, and it promises 1compare satisfies its specification14static int min(int a, int b) {
15 int r = a;
16 if (a <= b) {
17 r = a;
18 }
19 return r;
20}6@Test
7void firstIsSmaller() {
8 assertEquals(1, Min.min(1, 2));
9}14static int min(int a, int b) {
{{ true }}
15 int r = a;
{{ r = a }}
16 if (a <= b) {
{{ r = a and a <= b }}
17 r = a;
{{ r = a and a <= b }}
18 }
{{ (r = a and a <= b) or (r = a and b < a) }}
{{ r = a and (a <= b or b < a) }}
{{ r = a }}
19 return r;
20}r = a is in both cases, and the rest is true of any two numbersif collapses to r = aa on every run; check that against the promisea when a <= b, and b when b < ab < a, the promise was b: false exactly on those statesmin(2, 1) is one10* y
11* 2 | 1
12* --------+-------- x
13* 3 | 4
14*
15* @param x the point's x coordinate
16* @param y the point's y coordinate
17* @requires x != 0 and y != 0
18* @return 1 if x > 0 and y > 0,
19* 2 if x < 0 and y > 0,
20* 3 if x < 0 and y < 0, and
21* 4 if x > 0 and y < 023static int quadrant(int x, int y) {
24 int q = 1;
25 if (x < 0) {
26 q = 2;
27 }
28 if (y < 0) {
29 q = q + 1;
30 }
31 return q;
32}ifs in a row: after each, a claim with a case per branch takenint q = 1;
if (x < 0) {
q = 2;
}
if (y < 0) {
q = q + 1;
}
return q;23static int quadrant(int x, int y) {
{{ x != 0 and y != 0 }}
24 int q = 1;
{{ x != 0 and y != 0 and q = 1 }}
25 if (x < 0) {
{{ x < 0 and y != 0 and q = 1 }}
26 q = 2;
{{ x < 0 and y != 0 and q = 2 }}
27 }
{{ (x < 0 and y != 0 and q = 2) or
(0 < x and y != 0 and q = 1) }} {{ (x < 0 and y != 0 and q = 2) or
(0 < x and y != 0 and q = 1) }}
28 if (y < 0) {
{{ (x < 0 and y < 0 and q = 2) or
(0 < x and y < 0 and q = 1) }}
29 q = q + 1;
{{ (x < 0 and y < 0 and q = 3) or
(0 < x and y < 0 and q = 2) }}
30 }
{{ (x < 0 and y < 0 and q = 3) or
(0 < x and y < 0 and q = 2) or
(x < 0 and 0 < y and q = 2) or
(0 < x and 0 < y and q = 1) }}
quadrant 4 is promised, and q = 2: this implication is false
31 return q;
32}0 < x and 0 < y: quadrant 1 is promised, and q = 1x < 0 and 0 < y: quadrant 2 is promised, and q = 2x < 0 and y < 0: quadrant 3 is promised, and q = 30 < x and y < 0: quadrant 4 is promised, and q = 2quadrant(1, -1) is there, and the method returns 2 on itquadrant{{ (b and A) or (not b and B) }}
if (b) {
{{ A }}
first
{{ Q }}
} else {
{{ B }}
second
{{ Q }}
}
{{ Q }}int q = 1;
if (x < 0) {
q = 2;
}
if (y < 0) {
q = q + 1;
}
return q;{{ (y < 0 and ((x < 0 and q = 2) or (0 < x and q = 3))) or
(0 < y and ((0 < x and q = 1) or (x < 0 and q = 2))) }}
28 if (y < 0) {
{{ (x < 0 and q = 2) or (0 < x and q = 3) }}
29 q = q + 1;
{{ (x < 0 and q = 3) or (0 < x and q = 4) }}
30 }
{{ (0 < x and 0 < y and q = 1) or (x < 0 and 0 < y and q = 2) or
(x < 0 and y < 0 and q = 3) or (0 < x and y < 0 and q = 4) }}
31 return q;
32}23static int quadrant(int x, int y) {
{{ x != 0 and y != 0 }}
does not imply the claim below: 0 < x, y < 0 is a counterexample
{{ (x < 0 and y != 0) or (0 < x and 0 < y) }}
24 int q = 1;
{{ (x < 0 and y != 0) or
(0 < x and ((y < 0 and q = 3) or (0 < y and q = 1))) }}
25 if (x < 0) {
{{ y != 0 }}
26 q = 2;
{{ (y < 0 and ((x < 0 and q = 2) or (0 < x and q = 3))) or
(0 < y and ((0 < x and q = 1) or (x < 0 and q = 2))) }}
27 }
{{ (y < 0 and ((x < 0 and q = 2) or (0 < x and q = 3))) or
(0 < y and ((0 < x and q = 1) or (x < 0 and q = 2))) }}quadrant returns what it promises0 < x and y < 0, the lower rightseconds, abs, and compare are proofsSummin and quadrantLeslie Lamport, "The Future of Computing: Logic or Biology" (2003), one of Reading 2's articles.
int wrapsabs without its precondition: n = -n leaves -2147483648 as it isabs(-2147483648) returns -2147483648!abs doesseconds computes in a longYour job is to deliver code that you know works.