SE 504
Solution of 2-Color version of the Dutch National Flag Problem

Here we supply a solution to the 2-color version of the Dutch National Flag problem. The purpose is to illustrate how to prove the correctness of a program in which an array is modified.

Recall that, in the 2-color version of the Dutch National Flag Problem, we are given an array in which every element satisfies exactly one of the predicates isRed() and isBlue(). The goal is to swap elements of the array so as to achieve a state in which no BLUE element precedes any RED element. Further, the location of the boundary between RED and BLUE elements is to be indicated by the variable k.

Preliminaries

As modifications to the array are restricted to swapping pairs of its elements, let us postulate the existence of a command (or subprogram, if you prefer to look at it that way) called swap. This command, which involves three parameters, has the following semantics:

wp.(swap.b.i.j).Q = Q(b := b(i,j : b.j, b.i))

where (provided that i=j implies E=F)

b(i,j:E,F).k  = { Eif i=k
Fif j=k       (+)
b.kotherwise

Note that this is simply an extension of the notation b(i:E) denoting an array exactly like b except at location i.

Using the if function, (+) can be written as follows:

b(i,j : E,F).k = if(i=k, E, if(j=k, F, b.k))    (*)

In terms of the wp function (also called a "predicate transformer"), we could say that

[wp.(swap.b.i.j).Q  ≡  Q(b := b(i,j : b.j, b.i)]

which is the same thing as saying that {Q(b := b(i,j : b.j, b.i)} swap.b.i.j {Q} is a valid Hoare Triple but that any weakening of its precondition would invalidate it.

Lemma 1: Let b' be an abbreviation for b(i,j : b.j, b.i). Then
(a) b'.i = b.j,
(b) b'.j = b.i,
(c) (∀k | 0≤k<#b ∧ k≠i ∧ k≠j : b'.k = b.k)

Proof:
(a)  b'.i

  =    < defn of b' >
 
     b(i,j : b.j, b.i).i
 
  =    < (*) >
 
     if(i=i, b.j, if(j=i, b.i, b.i))
 
  =    < reflexivity of = >

     if(true, b.j, if(j=i, b.i, b.i))

  =    < (if.0a) if(true, y, z) = y > 

     b.j
(b)  b'.j

  =    < defn of b' >

     b(i,j : b.j, b.i).j

  =    < (*) >

     if(i=j, b.j, if(j=j, b.i, b.j))

  =    < reflexivity of = >

     if(i=j, b.j, if(true, b.i, b.j))

  =    < (if.0a) if(true, y, z) = y >

     if(i=j, b.j, b.i)

  .... continued below

To complete the proof of (b), we consider the two cases i≠j and i = j (which cover all possibilities, of course): the proof continues as follows:
Case i≠j:
     if(i=j, b.j, b.i)

  =    < assumption i ≠ j >

     if(false, b.j, b.i)

  =    < (if.0b) if(false, y, z) = z >

     b.i
Case i=j:
     if(i=j, b.j, b.i)

  =    < assumption i = j, (Gries 3.84a) >

     if(true, b.i, b.i)

  =    < (if.0a) if(true, y, z) = y >

     b.i

In effect, we've proved both (i=j) ⇒ (b'.j = b.i) and (i ≠ j) ⇒ (b'.j = b.i), which is to say that we've proved their conjunction. As this conjunction is equivalent to b'.j = b.i (as shown immediately below), this suffices as a proof of b'.j = b.i.

Theorem: (p ⇒ q) ∧ (¬p ⇒ q) = q
Proof:
     (p ⇒ q) ∧ (¬p ⇒ q) 

  =    < (Gries 3.78), with p,q,r := p,¬p,q >

     (p ∨ ¬p) ⇒ q

  =    < (Gries 3.28, Metatheorem 3.7, 3.4) >

     true ⇒ q

  =    < (Gries 3.73) >

     q

Instantiating this theorem with p,q := i=j, b'.j = b.i we get

((i=j) ⇒ (b'.j = b.i) ∧ (¬(i = j) ⇒ (b'.j = b.i))) = (b'.j = b.i)

Recognizing that ¬(i=j) = (i ≠ j), we see that the claim has been proved.

As for (c), by Gries's and Schneider's Metatheorem 9.16 (and remarks just before the end of Section 9.1), it suffices to let k be an arbitrary integer satisfying the range of the universal quantifier (i.e., 0≤k<#b ∧ k≠i ∧ k≠j) and to show that, for this value of k, the body holds. In other words, we show

(0≤k<#b ∧ k≠i ∧ k≠j) ⇒ (b'.k = b.k)

Proof: Assume 0≤k<#b, k≠i, and k≠j.
     b'.k 

  =    < defn of b' >

     b(i,j : b.j, b.i).k

  =    < (*) >

     if(i=k, b.j, if(j=k, b.i, b.k))

  =    < assumptions k≠i and k≠j >

     if(false, b.j, if(false, b.i, b.k))

  =    < (if.0b) if(false, y, z) = z (applied to nested if) >

     if(false, b.j, b.k)

  =    < (if.0b) if(false, y, z) = z >

     b.k

Corollary (to Lemma 1): perm.b.b' (i.e., b' is a permutation of b)

Before giving the program (and relevant parts of its correctness proof), we give another result that will be useful in carrying out that proof:

Lemma 2: The following is a valid step in an "equational logic style" proof:
     A ∧ B

  =    < A ⇒ (B = C) >

     A ∧ C
Proof: It follows from Gries (3.62): (A ⇒ (B = C)) = ((A ∧ B) = (A ∧ C))


Finally, the Program

Here is a program solving the 2-color version of the Dutch National Flag problem. The manner in which the loop invariant and loop guard arose were to replace k in the third conjunct of postcondition Q by fresh variable m and to add k = m as a new conjunct, yielding strengthened postcondition Q'. The negation of the new conjunct was the basis for the loop guard.

 |[var b : array of T;  { b = B }
   var k : int;
   var m : int;
   k, m := 0, #b;

   { loop invariant I: I1 ∧ I2 ∧ I3 ∧ I4, where
        I1 : perm.b.B
        I2 : 0≤k≤m≤#b
        I3 : (∀i | 0≤i<k : isRed(b.i))
        I4 : (∀i | m≤i<#b : isBlue(b.i))  }
   { bound function t: m-k }
   do k ≠ m ∧ isRed(b.k)  ⟶  k := k+1;
   [] k ≠ m ∧ isBlue(b.k) ⟶  swap.b.k.(m-1); m := m-1; 
   od
   { Q': perm.b.B  ∧  (∀i | 0≤i<k : isRed(b.i))  ∧  (∀i | m≤i<#b : isBlue(b.i)) ∧ k=m }
   { Q : perm.b.B  ∧  (∀i | 0≤i<k : isRed(b.i))  ∧  (∀i | k≤i<#b : isBlue(b.i))}
 ]|

Letting B0 and B1 denote the two guards of the loop, to prove the program's correctness, we would prove each of the following:

(i) {b=B} k,m := 0,#b {I}
(ii)(a) {I ∧ B0} k := k+1 {I}
(ii)(b) {I ∧ B1} swap.b.k.(m-1); m := m-1 {I}
(iii) [I ∧ ¬(B0 ∨ B1) ⇒ Q]
(iv) [I ∧ (B0 ∨ B1) ⇒ t>0]
(v)(a) {I ∧ B0 ∧ t=C} k := k+1 {t<C}
(v)(b) {I ∧ B1 ∧ t=C} swap.b.k.(m-1); m := m-1 {t<C}

As here we are focusing upon proofs involving code segments in which arrays are modified, the only items of interest to us are (ii)(b) and (v)(b). Proofs of the remaining items are similar to many that you have already seen.

(ii)(b) {I ∧ B1} swap.b.k.(m-1); m := m-1 {I}
Proof:
Assume I and B1. The conjuncts of I are
I1 : perm.b.B,
I2 : 0≤k≤m≤#b,
I3 : (∀i | 0≤i<k : isRed(b.i)),
I4 : (∀i | m≤i<#b : isBlue(b.i))

    wp.(swap.b.k.(m-1); m := m-1).I

  =   < wp catentation law >

    wp.(swap.b.k.(m-1)).(wp.(m:=m-1).I)

  =   < wp assignment law >

    wp.(swap.b.k.(m-1)).I(m:=m-1)

  =   < wp swap law >

    I(m:=m-1)(b := b(k,m-1 : b.(m-1),b.k))

  =   < abbreviate b(k,m-1 : b.(m-1),b.k)) as b' >

    I(m:=m-1)(b:=b')

  =   < defn of I; textual sub distributes over ∧ (and other operators) >

    (I1(m:=m-1) ∧ I2(m:=m-1) ∧ I3(m:=m-1) ∧ I4(m:=m-1))(b := b')

  =   < m does not occur in I1, I3 >

    (I1 ∧ I2(m:=m-1) ∧ I3 ∧ I4(m:=m-1))(b := b')

  =   < textual sub distributes over ∧ >

    I1(b:=b') ∧ I2(m:=m-1)(b:=b') ∧ I3(b:=b') ∧ I4(m:=m-1)(b:=b')

  =   < defn of I1, ..., I4 and text. sub. >

    perm.b'.B ∧ 0≤k≤m-1≤#b' ∧ (∀i | 0≤i<k : isRed(b'.i)) ∧ (∀i | m-1≤i<#b' : isBlue(b'.i)) 

  =   < perm.b'.B follows from transitivity of perm, given that perm.b'.b
        (by Corollary of Lemma 1) and perm.b.B (assumption); (3.39) >

    0≤k≤m-1≤#b' ∧ (∀i | 0≤i<k : isRed(b'.i)) ∧ (∀i | m-1≤i<#b' : isBlue(b'.i))

  =   < 0≤k is assumed; m-1≤#b' follows from assumption m≤#b and fact #b = #b' >

    k≤m-1 ∧ (∀i | 0≤i<k : isRed(b'.i)) ∧  (∀i | m-1≤i<#b' : isBlue(b'.i)) 

  =   < by the Corollary to Lemma 3 (see below), k≤m-1 (the 1st conjunct)
        implies R = R(b':=b), where R is the 2nd conjunct above;
        hence, by Lemma 2 we may replace R by R(b':=b)     >

    k≤m-1 ∧ (∀i | 0≤i<k : isRed(b.i)) ∧  (∀i | m-1≤i<#b' : isBlue(b'.i)) 

      < 2nd conjunct is assumption I3 >

    k≤m-1 ∧ (∀i | m-1≤i<#b' : isBlue(b'.i))

  =   < Split off term (8.23); range non-emptiness follows from assumption m≤#b and #b=#b') >

    k<=m-1  ∧  isBlue(b'.(m-1))  ∧  (∀i | m<=i<#b' : isBlue(b'.i)) 

  =   < b'.(m-1) = b.k (by part (b) of Lemma 1) and 
        isBlue(b.k) (by assumption); it follows that isBlue(b'.(m-1)) >

    k≤m-1 ∧ (∀i | m≤i<#b' : isBlue(b'.i)) 

  =   < by the Corollary to Lemma 4, k≤m-1 (the 1st conjunct)
        implies R = R(b':=b), where R is the 2nd conjunct above;
        hence, by Lemma 2 we may replace R by R(b':=b)     >

    k≤m-1 ∧ (∀i | m≤i<#b : isBlue(b.i)) 

  =   < assumption I4; (Gries 3.39) >

    k≤m-1

  =   < assumptions k≤m, k≠m >

    true


Lemma 3: k≤m-1 ⇒ (∀i | 0≤i<k : b'i = b.i)
Proof:
Assume k≤m-1.
    true

  =   < Lemma 1, part (c), applied to our b' >

    (∀i | 0≤i<#b ∧ i≠k ∧ i≠m-1 : b'.i = b.i)

  =   < Range split (Gries 8.16) >

    (∀i | 0≤i<k ∧ i≠k ∧ i≠m-1 : b'.i = b.i)  ∧
    (∀i | k≤i<#b ∧ i≠k ∧ i≠m-1 : b'.i = b.i) 

 ==>  < Gries 3.76b >

    (∀i | 0≤i<k ∧ i≠k ∧ i≠m-1 : b'.i = b.i) 

  =   < i<k implies i≠k, so can use (A ==> B) = (A ∧ B = A) (Gries 3.60) >

    (∀i | 0≤i<k ∧ i≠m-1 : b'.i = b.i) 

  =   < i<k together with assumption k≤m-1 implies i≠m-1,
        so once again (Gries 3.60) can be applied           >

    (∀i | 0≤i<k : b'.i = b.i) 

Corollary: k≤m-1 ⇒ ((∀i | 0≤i<k : isRed(b'.i)) = (∀i | 0≤i<k: isRed(b.i)))
Proof:
Assume k≤m-1.
    (∀i | 0≤i<k : isRed(b'.i))

  =   < assumption k≤m-1 and Lemma 3; Gries (3.39) >

    (∀i | 0≤i<k : isRed(b'.i))  ∧  (∀i | 0≤i<k : b'.i = b.i)

  =   < Distributivity (Gries 8.15) >

    (∀i | 0≤i<k : isRed(b'.i)  ∧  b'.i = b.i)

  =   < Substitution (Gries 3.84a) >

    (∀i | 0≤i<k : isRed(b.i) ∧  b'.i = b.i)

  =   < Distributivity (Gries 8.15) >

    (∀i | 0≤i<k : isRed(b.i)) ∧  (∀i | 0≤i<k : b'.i = b.i)

  =   < assumption k≤m-1 and Lemma 3; Gries (3.39) >

    (∀i | 0≤i<k : isRed(b.i)) 

Lemma 4: k ≤ m-1 ⇒ (∀i | m≤i<#b : b'.i = b.i)
Proof: Similar to that of Lemma 3.

Corollary: k ≤ m-1 ⇒ ((∀i | m≤i<#b' : isBlue(b'.i)) = (∀i | m≤i<#b: isBlue(b.i)))
Proof: Similar to that of Corollary to Lemma 3.


(v)(b) {I ∧ B1 ∧ t=C} swap.b.k.(m-1); m := m-1 {t<C}
Proof:
Assume I, B1, and t=C.
     wp.(swap.b.k.(m-1); m := m-1).(t<C)

  =   < wp catenation law >

    wp.(swap.b.k.(m-1).(wp.(m := m-1).(t<C))

  =   < wp assignment law, defn of t >

    wp.(swap.b.k.(m-1)).(m-1-k < C)

  =   < wp swap law >

    (m-1-k < C)(b := b(k, m-1 : b.(m-1), b.k))

  =   < text. sub. >

    m-1-k < C

  =   < assumption m-k = C >

    m-1-k < m-k

  =   < algebra >

    -1 < 0

  =   < arithmetic >

    true