2011/06/26

The Dutch National Flag Problem, inductively

Jeremy suggested that I look into Wouter Swierstra's Agda solution to the Dutch National Flag problem, which Shin has also written about in his Chinese blog using Dijkstra's guarded command language. There are two things that I'd like to improve:

  1. In Agda every program has to be terminating, and one (perhaps the most obvious) way to show that is to make a program structurally recursive. Wouter did it by introducing an explicit tree-shaped call structure on which the program recurses, which is a general technique (the so-called Bove-Capretta method) for expressing well-founded general recursion in a total language. For this problem, however, it is obvious that the difference between the two indices j and k is decreasing, so we should be able to just do structural recursion on the difference. It would be a counterexample against using a total language if we could not express that directly and had to invent a tree structure that does not seem necessary.
  2. Wouter then wrote additional proofs to verify the program in the traditional index-based style. That is, he worked in the externalist way. I would like to see an internalist solution, i.e., the proof being integrated into the program.
It turned out that there is an arguably cleaner solution — a few functional programs that use exclusively pattern matching!

First we look at the problem of expressing the program as structurally recursive. Wouter used the finite numbers as indices into vectors (naturally), and he defined a Difference relation on finite numbers as

data Difference : forall {n} -> (i j : Index n) -> Set where
  Same : forall {n} -> (i : Index n) -> Difference i i
  Step : forall {n} -> (i j : Index n) -> 
    Difference i j -> Difference (inj i) (Next j)
The reason that the program cannot be made structurally recursive using this type is that the section of unknown colour can shrink either leftwards or rightwards, i.e., the pair of indices inj i and Next j can proceed to either inj i and inj j or Next i and Next j. For the latter case, we need a separate function to tweak the difference, but then Agda cannot see that the tweaked difference is smaller than the given one. We do know, however, that the "size" of the difference, i.e., the number of Steps, is smaller, so all we need to do is expose that size, i.e., the underlying natural number, as an index in the type, using (ornamental-) algebraic ornamentation:
data Difference : forall {n} -> (i j : Index n) -> (m : Nat) -> Set where
  Same : forall {n} -> (i : Index n) -> Difference i i Zero
  Step : forall {n} -> (i j : Index n) -> forall {m} ->
    Difference i j m -> Difference (inj i) (Next j) (Succ m)
Now the program can just do structural recursion on m. No matter how the difference is tweaked, the size (reflected in the index of its type) can always be shown to be smaller, so Agda accepts the program as structurally recursive and thus terminating. This datatype will also appear in my program below (with a different syntax).

The second problem is whether we can make the program manifest its own correctness (so separate proofs are not needed) by making the types more precise. The crucial property we wish to express is the invariant that the array is always divided into four sections such that pebbles in the first/second/fourth section are all red/white/blue while those in the third section can be arbitrarily coloured (in red, white, or blue). It turned out that this invariant can be formulated inductively and baked into the list type. Assume there is a three-element datatype for the colours,

data Colour : Set where
  red white blue : Colour
and the type of pebbles is a type family Peb : Colour → Set. The Dutch vectors are defined by
data DVec : (i j k : ℕ) → Set where
  []   : DVec 0 0 0
  _R∷_ : (x : Peb red) →     ∀ {i j k} (xs : DVec i j k) → DVec (suc i) (suc j) (suc k)
  _W∷_ : (x : Peb white) →   ∀ {  j k} (xs : DVec 0 j k) → DVec 0       (suc j) (suc k)
  _A∷_ : ∀ {c} (x : Peb c) → ∀ {    k} (xs : DVec 0 0 k) → DVec 0       0       (suc k)
  _B∷_ : (x : Peb blue) →              (xs : DVec 0 0 0) → DVec 0       0       0 
It is easy to see from the indices that once we use the arbitrary cons _A∷_ it is no longer possible to use the blue cons _B∷_ (since the index k in the type of a Dutch vector constructed by _A∷_ is nonzero but _B∷_ expects k in the type of its tail to be 0), and once we use the white cons _W∷_ it is no longer possible to use _A∷_ and _B∷_, etc. Thus these constructors necessarily appear in the desired order. Moreover, the indices in the type of a Dutch vector are exactly the values of the variables indexing into the corresponding array satisfying the invariant. And interestingly, we can simply use natural numbers instead of finite numbers as the indices; in the type of a successfully constructed Dutch vector, the indices are necessarily within bounds.

Next we need to expose two more pieces of information in the indices, both of which are algebraic ornamentations. The first one is the termination measure, i.e., the difference of k and j. We redefine the datatype Difference as

data _-_≈_ : ℕ → ℕ → ℕ → Set where
  zero : ∀ {j} → j - j ≈ zero
  suc  : ∀ {j k m} → k - j ≈ m → suc k - j ≈ suc m
The difference can then be computed by the fold,
diff : ∀ {i j k} → DVec i j k → Σ ℕ (λ m → k - j ≈ m)
diff []        = _ , zero
diff (x R∷ xs) = _ , adjustLeft (suc (proj₂ (diff xs)))
diff (x W∷ xs) = _ , adjustLeft (suc (proj₂ (diff xs)))
diff (x A∷ xs) = _ , suc (proj₂ (diff xs))
diff (x B∷ xs) = _ , zero
where the function adjustLeft is defined by
adjustLeft : ∀ {j k m} (d : k - j ≈ suc m) → k - suc j ≈ m
adjustLeft {m = zero } (suc zero) = zero
adjustLeft {m = suc _} (suc d) = suc (adjustLeft d)
One can see that the definition is a bit tricky as it is structurally recursive on m instead of d. This will turn out to be useful later. After the algebraic ornamentation, the signature of the DVec type becomes
DVec : (i j k : ℕ) → ∀ {m} (d : k - j ≈ m) → Set
Another information we need is the colour of the first pebble in the arbitrarily coloured section, as it determines what kind of swap we will do. There may or may not be such a colour, depending on the size of the arbitrarily coloured section, which is m. So we define
MaybeColour : ℕ → Set
MaybeColour zero = ⊤
MaybeColour (suc _) = Colour
and perform algebraic ornamentation using the fold
firstArbitraryColour : ∀ {i j k m} {d : k - j ≈ m} → DVec i j k d → MaybeColour m
firstArbitraryColour []              = tt
firstArbitraryColour (x R∷ xs)       = firstArbitraryColour xs
firstArbitraryColour (x W∷ xs)       = firstArbitraryColour xs
firstArbitraryColour (_A∷_ {c} x xs) = c
firstArbitraryColour (x B∷ xs)       = tt
So the signature of the type of the Dutch vectors becomes
DVec : (i j k : ℕ) → ∀ {m} (d : k - j ≈ m) → MaybeColour m → Set

Now we are ready to write the swaps. First we consider the case where the first arbitrary coloured pebble is actually red, i.e., we need to complete the program

reduceRed :
  ∀ {i j k m} {d : k - j ≈ suc m} →
  (xs : DVec i j k d red) → DVec (suc i) (suc j) k (adjustLeft d) (nextColour xs)
reduceRed xs = ?
where nextColour computes the first colour of the arbitrarily coloured section after it is reduced. Note that in the type of reduceRed, the size of d is suc m, so we are allowed to specify that the first colour of xs is red. Asking Agda to perform case analysis, we get
reduceRed :
  ∀ {i j k m} {d : k - j ≈ suc m} →
  (xs : DVec i j k d red) → DVec (suc i) (suc j) k (adjustLeft d) (nextColour v)
reduceRed (x R∷ xs) = ?
reduceRed (x W∷ xs) = ?
reduceRed (x A∷ xs) = ?
In the red cons case, we should go past x and reduce the rest, so the goal is solved by x R∷ reduceRed xs. Here the typing works directly because of how we defined adjustLeft: Matching the input vector with red cons unifies d with adjustLeft (suc d') for some d' (of size suc m) appearing in the type of xs, so the difference in the goal type becomes adjustLeft (adjustLeft (suc d')), while in the type of x R∷ reduceRed xs it is adjustLeft (suc (adjustLeft d')), so we need the emphasised subterm in the goal type to compute further. We could have done case analysis on d' but that messes up the program; instructing adjustLeft to look at the size instead of the difference itself, however, directly makes the emphasised subterm compute.
reduceRed :
  ∀ {i j k m} {d : k - j ≈ suc m} →
  (xs : DVec i j k d red) → DVec (suc i) (suc j) k (adjustLeft d) (nextColour v)
reduceRed (x R∷ xs) = x R∷ reduceRed xs
reduceRed (x W∷ xs) = ?
reduceRed (x A∷ xs) = ?
In the white cons case, we need to swap x with the first arbitrarily coloured pebble, which we assume to be red, and change the constructor to a red cons, extending the red section. We thus need to define a function to lookup the pebble
firstPeb : ∀ {j k m} {d : k - j ≈ suc m} → DVec zero j k d red → Peb red
firstPeb (x W∷ xs) = firstPeb xs
firstPeb (x A∷ xs) = x
and a function that substitutes the white pebble x for that pebble in xs.
substWhite :
  ∀ {n j k m} {d : k - j ≈ suc m} →
  Peb white → (xs : DVec n zero j k d red) →
  DVec n zero (suc j) k (adjustLeft d) (nextColour xs)
substWhite x (y W∷ xs) = y W∷ substWhite x xs
substWhite x (y A∷ xs) = x W∷ xs
The case is then solved by firstPeb xs R∷ substWhite x xs.
reduceRed :
  ∀ {i j k m} {d : k - j ≈ suc m} →
  (xs : DVec i j k d red) → DVec (suc i) (suc j) k (adjustLeft d) (nextColour v)
reduceRed (x R∷ xs) = x R∷ reduceRed xs
reduceRed (x W∷ xs) = firstPeb xs R∷ substWhite x xs
reduceRed (x A∷ xs) = ?
For the last goal we simply replace the arbitrary cons with a red cons.
reduceRed :
  ∀ {i j k m} {d : k - j ≈ suc m} →
  (xs : DVec i j k d red) → DVec (suc i) (suc j) k (adjustLeft d) (nextColour v)
reduceRed (x R∷ xs) = x R∷ reduceRed xs
reduceRed (x W∷ xs) = firstPeb xs R∷ substWhite x xs
reduceRed (x A∷ xs) = x R∷ xs
Similarly, reduceWhite and reduceBlue can be defined by just pattern matching, although reduceBlue is slightly more complex. The three functions can be assembled into a tail-recursive function
reduce :
  ∀ {i j k m} {d : k - j ≈ m} {c} →
  DVec i j k d c → Σ[ i' ∶ ℕ ] Σ[ j' ∶ ℕ ] DVec i' j' j' zero tt
reduce {m = zero} {d = zero} v = _ , _ , v
reduce {m = suc _} {c = red  } v = reduce (reduceRed   v)
reduce {m = suc _} {c = white} v = reduce (reduceWhite v)
reduce {m = suc _} {c = blue } v = reduce (reduceBlue  v)
which is structurally recursive on m. We can package reduce so it works on ordinary lists:
fuel : ∀ {k} →  k - zero ≈ k
fuel {zero } = zero
fuel {suc n} = suc (fuel {n})

initialise :
  (xs : List (Σ Colour Peb)) →
  let n = length xs in Σ[ c ∶ MaybeColour n ] DVec zero zero n fuel c
initialise [] = tt , []
initialise ((c , x) ∷ xs) = c , x A∷ proj₂ (initialise xs)

forget : ∀ {i j k m} {d : k - j ≈ m} {c} → DVec i j k d c → List (Σ Colour Peb)
forget [] = []
forget (x R∷ xs) = (red   , x) ∷ forget xs
forget (x W∷ xs) = (white , x) ∷ forget xs
forget (x A∷ xs) = (_     , x) ∷ forget xs
forget (x B∷ xs) = (blue  , x) ∷ forget xs

dutchFlag : List (Σ Colour Peb) → List (Σ Colour Peb)
dutchFlag = forget ∘ (proj₂ ∘ proj₂ ∘ reduce) ∘ (proj₂ ∘ initialise)
The whole development is available here. I think the novelty of the approach is that the invariant is formulated inductively and integrated into the list type, so the proofs that certain swaps preserve the invariant can be written inductively in the form of simple list-manipulating functional programs. Also it demonstrates how algebraic ornamentation can be used to tidy up dependently typed programs.

--
Still five more blog posts in the queue, argh..

Labels:

2009/03/08

第一次跑 Agda 程式

正常人如果知道我們這群寫 Agda 程式的人其實都不跑我們寫的程式,通過 typecheck 就算了,一定覺得不可思議 XD。今天我終於跑了第一個 Agda 程式,依照傳統當然是寫

module Hello where

open import IO
open import Data.String

main = run (putStrLn "hello, world")
點選 Agda mode 的 "Compile (C-c C-x C-c)" 或進入 terminal 下達 "agda -c" 指令就可以編譯成執行檔。不過編出來的 Haskell code 全是 unsafeCoerce,是怎麼一回事?XD

--
有沒有人要用 Agda 嚴謹地寫個一千題 "ACM"?XD

Labels:

2009/01/31

Algebra of programming in Agda: dependent types for relational program derivation

中文摘要:

關係式程式推導(relational program derivation)是「以代數規則將關係式規格(relational specification)逐步特化為程式」的技巧。因為建構過程的保證,如此得到的程式必然是正確的。另一方面,依值型別理論(dependent type theory)已發展得十分豐富,足以表達多種正確性質且可透過型別檢驗加以驗證。

我們造了一套程式庫 AoPA,使用者可在支援依值型別的程式語言 Agda 中編寫關係式推導。每個程式都附帶一個代數推導,由型別系統擔保其正確性。

我們提出兩個有意思的例子:一個最佳化問題和快速排序的推導,後者用良基遞迴(well-founded recursion)在支援歸納式型別(inductive types)的語言中模映「計算終必停止的生滅式」(terminating hylomorphisms)。

嘔,好噁心的翻譯(特別是把所有名詞都譯出來之後 ─「生滅式」還滿惡搞的 XD)。要看原文摘要和 paper 請到 scm 老師的官方網頁

--
是太久沒翻譯還是這段本來就難譯啊?不知道這個問題的答案代表可能是前者 XD。

Labels: , ,

2008/09/04

Accessibility and Strong Induction

Accessibility becomes more accessible if one compares it with the proof of "induction implies strong induction". The ordinary induction principle on natural numbers is

Note that when proving P(n+1) we can only presuppose P(n). On the other hand, strong induction permits us presuppose P(0) through P(n) when proving P(n+1):

The two induction principles are equivalent. Proof of "strong induction implies ordinary induction" is trivial. As for the reverse direction, we can first rewrite "∀k<n. P(k)" in the premise of strong induction more intuitively as Q(n) = P(0) ∧ P(1) ∧ ... ∧ P(n-1). The strategy now is proving "∀n. Q(n)" from the assumption "Q(n) => P(n)" by ordinary induction, and then applying the premise to every Q(n) and get "∀n. P(n)". The proof goes:

  • When n = 0, Q(0) is vacuously true.
  • Suppose Q(n) = P(0) ∧ P(1) ∧ ... ∧ P(n-1) holds. Since Q(n) => P(n), we have P(n) as well. It follows that Q(n+1) = Q(n) ∧ P(n) also holds.

Now compare with the Agda definition of accessibility:

data Acc {A : Set} (_>_ : A -> A -> Set) (x : A) : Set where
  acc : (forall y -> x > y -> Acc _>_ y) -> Acc _>_ x
The proposition which accessibility wishes to prove is a bit weird, though (since it's recursively defined). Comparing with the proof above and roughly speaking, P and Q are both Acc _>_, i.e., Q(n) = Q(0) ∧ Q(1) ∧ ... ∧ Q(n-1). Although what acc encapsulates is a dependent function and not the more familiar form of product, but the effect of (x : A) -> B x is just using x to index one member of some set of B's and therefore encoding a (possibly infinite) product. For this reason, dependent functions are also called dependent products.

We call a relation R over A is well-founded if every element of A is accessible under R.

well-found : {A : Set} -> (A -> A -> Set) -> Set
well-found R = forall x -> Acc R x
The _≤_ proposition on natural numbers can be defined as
data _≤_ : ℕ -> ℕ -> Set where
  ≤-refl : {n : ℕ} -> n ≤ n
  ≤-step : {m n : ℕ} -> m ≤ n -> m ≤ suc n
in which suc n means the successor of n (i.e., 1+n). Define m > n = suc n ≤ m. We can prove that _>_ is well-founded:
ℕ>-wf : well-found _>_
According to the definition of well-found, we have to prove that Acc _>_ n holds for all n. Given n, the only way to prove Acc _>_ n is to prove Acc _>_ 0Acc _>_ 1 ∧ ... ∧ Acc _>_ (n-1) (this is what the constructor acc says), which is represented by dependent functions in Agda --- given an index m, return Acc _>_ m.
conjunction : forall n -> forall m -> n > m -> Acc _>_ m
Now we imitate the proof above and do ordinary induction on n. When n = 0 the proposition is vacuously true:
conjunction zero _ ()
Suppose conjunction n = Acc _>_ 0Acc _>_ 1 ∧ ... ∧ Acc _>_ (n-1) holds. What we wish to construct is a product Acc _>_ 0Acc _>_ 1 ∧ ... ∧ Acc _>_ (n-1)Acc _>_ n, encoded as a dependent function. Given an index m, we have to hand out the corresponding Acc _>_ m, so we do case analysis on m. The last term Acc _>_ n is constructed from conjunction n:
conjunction (suc n) .n ≤-refl = acc (conjunction n)
For the other indices we just take the corresponding term in conjunction n:
conjunction (suc n) m (≤-step n>m) = conjunction n m n>m
Finally let's return to the well-foundedness of _>_. Since we can prove conjunction n for any n,
ℕ>-wf n = acc (conjunction n)

--
關於 accessibility 的直白解釋和使用例子,請期待〈Algebra of Programming using Dependent Types〉journal 版!(我對於「什麼時候才會公開」沒有概念 XD。)

Labels:

2008/05/15

Happy Programming

Although Agda hasn't supported universe polymorphism, recently an option --type-in-type is added to enable impredicativity. Modelling sets is now much, much easier. Defining the type of as Set -> Set, the type of is now simply A ← ℙ A, and the relation subset = ∈ ﹨ ∈ also has a simple type ℙ A ← ℙ A. All those annoying 's are gone! Unfortunately, going back to impredicativity would make the type system inconsistent, which means every proposition can be proved in the system and the system would be considered unreliable. Therefore it is not a good idea for AoPA to switch to "impredicative sets", I guess?

--
That's unfortunate...

Labels:

2008/04/22

An Attempt

It's time to return to AoPA and proceed with defining all the remaining relational operators, in hope of finally solving optimisation problems! It seems that when division comes into play, the problem about predicativity becomes even severer. Shin said "Since the only arrow having type ←₁ is ∈, we may be able to create specialised version of division to get around such problems?", but I don't quite understand what he means. (XD) Instead I wondered whether it is possible to make the sets ordinary values and thus eliminating the need for multiple versions of arrows, compositions, etc. As an inexperienced dependently-typed programmer, the only thing I can do is trying. So here it goes.

Relations are still defined in the same way.

_←_ : Set -> Set -> Set1
B ← A = A -> B -> Set
In order to make the type of sets a member of Set, I tried the following definition:
data §et (A : Set) (P : A -> Set) : Set where
  set : §et A P
The information (proposition) that characterises the set is put in the type. Some operations about sets can be easily implemented. Since all the relevant information is in the types, the sole purpose of the terms is carrying the types.
_∪_ : {A : Set} {P Q : A -> Set} ->
      §et A P -> §et A Q -> §et A (\a -> P a ⊎ Q a)
set ∪ set = set

_∩_ : {A : Set} {P Q : A -> Set} ->
      §et A P -> §et A Q -> §et A (\a -> P a × Q a)
set ∩ set = set

∈ : {A : Set} {P : A -> Set} -> A ← §et A P
∈ {P = P} set a = P a

Λ : {A B : Set} -> (R : B ← A) -> (a : A) -> §et B (R a)
Λ R a = set
Inclusion can now be defined as a relation between sets.
_⊆_ : {A : Set} {P Q : A -> Set} -> §et A Q ← §et A P
_⊆_ {P = P} {Q = Q} set set = forall a -> P a -> Q a
When I'm proving its reflexivity and transitivity, this definition makes Agda's pattern matching mechanism behave in a way I've never seen before. Fortunately it doesn't cause much problem.
⊆-refl : {A : Set} {P : A -> Set} {s : §et A P} -> s ⊆ s
⊆-refl {s = set} = \a Pa -> Pa

⊆-trans : {A : Set} {P₁ P₂ P₃ : A -> Set}
          {s₁ : §et A P₁} {s₂ : §et A P₂} {s₃ : §et A P₃} ->
          s₁ ⊆ s₂ -> s₂ ⊆ s₃ -> s₁ ⊆ s₃
⊆-trans {s₁ = set} {s₂ = set} {s₃ = set} s₁⊆s₂ s₂⊆s₃ =
  \a P₁a -> s₂⊆s₃ a (s₁⊆s₂ a P₁a)
So far so good. But difficulty emerged when I was trying to define relational fold. It's rather impossible to assign a type to the fold operator. Therefore I naturally stopped here.

I would guess this approach is very likely to fail. Even if foldR is successfully modelled, the entire AoPA has to be revised, which is a rather daunting task. XD

--
So it seems that we have to live with predicativity and all its consequences after all...?

Labels:

2008/04/12

Algebra of Programming using Dependent Types

出現在 scm 老師的網頁上了!!!

S-C. Mu, H-S. Ko, and P. Jansson. Algebra of programming using dependent types. In Mathematics of Program Construction 2008. July 2008.

是完美的 16 頁 XD。

--
晚點再看一遍 XD。

Labels: ,

2008/02/16

Adventure in Oxford

我懶得寫了耶,反正看到很多東西,Oxford 的環境很不錯 XD。scm 老師的朋友寫了一張參觀路線給我,還借我他的職員證,所以我可以憑證進入各個學院(不然要繳錢或根本進不去)。中間 scm 老師的學妹帶我進她的學院吃中餐(就是 Harry Potter 電影裡的餐廳!)以及參觀。總之看了幾個學院、參觀兩個博物館、和 Oxford 一些其他的地標。Postcards 我剛剛弄好了,明天丟進郵筒裡。

回來突然想到 Jeremy 昨天講的話,試著改用 universal property(的一個方向)證明 fold fusion,果然一句話就解決了,因為 induction 現在藏在 universal property 的證明裡面。

foldr-univr : {A B : Set} -> (h : [ A ] -> B) -> forall f e ->
  (h [] ≡ e) -> (forall x xs -> h (x ∷ xs) ≡ f x (h xs)) ->
  (forall x -> h x ≡ foldr f e x)
foldr-univr h f e base-cond step-cond []       = base-cond
foldr-univr h f e base-cond step-cond (x ∷ xs) =
  ≡-trans (step-cond x xs)
          (≡-cong (f x) (foldr-univr h f e base-cond step-cond xs))

foldr-fusion : {A B C : Set} -> (h : B -> C) -> {f : A -> B -> B} ->
  {g : A -> C -> C} -> {e : B} -> (forall x y -> h (f x y) ≡ g x (h y)) ->
  forall x -> (h ∘ foldr f e) x ≡ foldr g (h e) x
foldr-fusion {_} {_} {_} h {f} {g} {e} fuse-cond =
  foldr-univr (h ∘ foldr f e) g (h e) ≡-refl
              (\x xs -> fuse-cond x (foldr f e xs))

--
快離開 Oxford 嘍。

Labels: ,

2007/12/28

重寫再重寫

改寫 pipe reasoning 使它也能處理 (B : Set) <- (A : Set1) 的東西,卻發現難以下手,直到我終於會用小黑點,才明白 scm 老師當初的苦心 XD。之後把所有的暴力證明全部用 pipe reasoning 重寫一遍,有比較好看一點。例如剛剛那個 subcase 現在變成

... | no  a≰b =
  a , b ∷ x
  ≫ combine → b ∷ insert a x by inj₂ (exists {_} {_} {b , insert a x}
                                   ((≡-refl , insert⊑combine (a , x)
                                   (insert a x) ≡-refl) , ≡-refl))
  → ordered? → b ∷ insert a x by (
      b ,₁ ordered? (insert a x)
      ≫₁ idR ⨉₁ ∈ → b , insert a x by ≡-refl , insert-respects-order
                                                 a x ordered-x-x
      → cons ○ (lbound ﹖) → b ∷ insert a x by (
          b , insert a x
          ≫ lbound ﹖ → b , insert a x by ≡-refl ,
                                          insert-respects-lbound 
                                            a b x lbound-b,x
                                            (<-relax (≰-elim a≰b))
          → cons → b ∷ insert a x by ≡-refl
          →∎)
      →∎)
  ↪ y by insert-a-b∷x≡y
  →∎

知道 pipe reasoning 在幹麼的話應該就比較容易看得懂,不過這只是形式上改寫,實質上做的事情是一樣的。(可是這種東西本來就是 formal?XD)scm 老師看到我寫出那團亂碼,特地寄來三頁代數證明,就甘心耶!所以再看看有沒有機會把它改成代數式證明。剛才把最新的 code 推上 darcs repository,發現 scm 老師也在剛剛把 paper revision 推上去,大家都在拚命工作 XD。

Projects 和 finals 紛紛出籠了,而且都不太溫和。從現在到學期末前應該沒一刻得閒。AI term project 要和不認識的人在一星期內搞定;DCL final project 還不知道難度;知識論期末考的題目有深度,不開始準備不行;宋明理學下半段已經談了程頤、朱熹,抓不太到綱領;作業系統和資料庫系統印象都模糊一片;圖論也不是讀得那麼顆粒分明。Anyway, let's do it.

--
覺得自己好弱喔,大三都過一半了,是不是覺悟得太晚了…

Labels: ,

2007/12/27

瘋了瘋了

ordered?foldR 定義之後,isort-der 到處都是一串 relation compositions,也就是有一大堆 nested existential quantifiers!現在已經瘋掉了,開始用湊的 XD。

--
又要產出一堆亂碼了 XD。

Labels:

2007/12/26

慢?

其實「慢?」也的確可以是個 coreflexive 啦 XD。不知道是因為我用了好幾層的 with,還是因為當初編譯有瑕疵,Agda mode 載入一次,就要好幾分鐘,而且 Leopard 會愈變愈慢 XD。

--
培養耐性?XD

Labels:

2007/12/19

背水一戰

和新版 ghc、Agda 以及眾 libraries 拚死一戰 XD。大致解讀關於 ghc 6.8.1 與 Leopard PPC 的討論串,似乎是 ghc 的 native code generator 產生的某種 code pattern 在 Leopard 下的詮釋有問題。討論串上好像有人用 -fvia-C option 編譯成功,所以我也依樣畫葫蘆,在 /usr/local/bin/ghc 裡面轉呼叫 ghc-6.8.1 時加上 -fvia-C option。奇蹟立刻發生了,binary、QuickCheck 等 libraries 不再出現惱人的 "unknown scattered relocation type 4",但 zlib 仍然在 preprocessing 時出現兩行,我只好先祈禱那不影響大局 XD。很可惜,稍後 Agda-2.1.3 雖然看起來編譯成功,Agda mode 卻果然當在那邊不動。多試了幾次以後,我對 zlib source 下指令 runhaskell Setup.hs build --verbose=3 看看到底是誰丟出錯誤訊息,發現兇手只可能是 hsc2hs。在直覺的引導下,我手動把那次 hsc2hs invocation 所用的 linker 從 ghc 改成 gcc 產生 Stream.hs,然後再次建造 zlib ─ 成功了!Agda-2.1.3 編譯再次成功,而且這次 Agda mode 也可以 work 了!!!現在總算可以開始做正事嘍 ─ 等明天圖論期中考考完馬上開始 XD。正事的第一步自然是好好地看一下 standard library 和 scm 老師近幾天生出來的好多 code,然後就可以試著把 insertion sort 湊完。

--
幸好沒掉到水裡 XD。現在趕快讓 Time Machine 備份 XD。


Time Machine 很快就派上用場,因為我大喜之下把先前我寫的 Agda files 刪個精光 XD。當然因為這些 code 大部份都貼到 blog 上了,所以沒有 Time Machine 也不至於太慘重 XD。

--
不過有 Time Machine 還是最方便 XD。

Labels: ,

2007/12/13

Counting Sort

〈Functional Algorithm Design〉最後一節的那三個 sorting algorithm derivation 實在好複雜,所以我想自己弄一個試試看。於是我挑上

perm : List Nat ← List Nat
perm = ((fun bagify) º) ○ (fun bagify)

這個定義來做。其中 bag 實作為一個 List (Nat × Nat),每個 pair 分別是一個數字和它出現的次數,整個 list 依照前者從小到大排列。(就是這裡作弊了 XD。)bagify 就定義成

bagify : List Nat -> List (Nat × Nat)
bagify = foldr consbag []
  where
    consbag : Nat -> List (Nat × Nat) -> List (Nat × Nat)
    consbag a [] = < a , 1 > :: []
    consbag a (< a' , c > :: x) =
        a == a' => < a' , suc c > :: x
      ! a >  a' => < a' ,     c > :: consbag a x
      ! otherwise  < a , 1 > :: < a' , c > :: x

把 specification ordered? ○ perm 展開成 ordered? ○ bagifyº ○ bagify 後,很顯然只能從前面兩個下手,而那一段很自然就是把 bag 鋪展成一個 sorted list。把這個動作叫做 unroll

unroll : List (Nat × Nat) -> List Nat
unroll = foldr dup []
  where
    dup : Nat × Nat -> List Nat -> List Nat
    dup < a , c > x = dup-term a c x
      where
        dup-term : Nat -> Nat -> List Nat -> List Nat
        dup-term a      0  x = x
        dup-term a (suc n) x = a :: dup-term a n x

就得到 counting sort unroll ○ bagify。雖然整個 derivation 平淡無奇,也沒用到 fold fusion,不過在其他 sorting 都還做不出來的情況下,先來試試也好 XD。

--
combine = cons ⊔ (cons ○ (idR ⨉ combine) ○ swap ○ (idR ⨉ (cons º))) 這種 relation 根本沒辦法動它呀 XD。

Labels: ,

Coding Week

本來跟 scm 老師說如果這兩週進度異常順利,才會有時間想想 relational derivation in Agda,結果不但進度異常不順利,還在課堂間偷寫了一點點 Agda XD。現在要在兩三天內把 AI task 1 和 DCL lab 5 生出來,非常緊了 XD。

Agda 的話其實只是證了一兩個簡單的定理,畢竟是課間弄出來的 XD。像 relational fold 可以精煉(refine):

rel-fold-refine : {A B : Set} ->
  (R₁ R₂ : B ← (A × B)) (S₁ S₂ : ℙ B) ->
    R₁ ⊒ R₂ -> S₁ ⊇ S₂ -> fold R₁ S₁ ⊒ fold R₂ S₂

然後夠精煉的 relational fold 就可以轉成 functional fold:

rel-fold-to-foldr : {A B : Set} -> (f : A -> B -> B) (e : B) ->
                      fold (fun (uncurry f)) (sing e) ⊒ fun (foldr f e)

其中 foldrData.List 裡面的版本,所以 f 外面要套上 uncurrysing 則是把一個元素轉成 singleton set。我覺得要從 ordered? ○ perm 導出 insertion sort 還是非常難 ─ perm 在上次 quick sort 大冒險的時候就已經證明是相當難搞的東西了 XD。

Relational composition 的 associativity 也讓我想了一陣,以致於圖論的第二節都聽不太懂 XD。本來想說是不是可以設法讓兩邊轉成某種 normal form(如 prenex normal form)然後推過去,最後把過程藏在 chain reasoning 下面變成自動的,可是弄一弄發現我不會給 type XD。

最後在 Agda Wiki 上面看到關於 universe polymorphism 的討論,感覺上和 '←'、'○' 的多版問題有關,不過不知道是不是真的有關 XD。

--
緊張刺激 XD。

Labels: ,

2007/12/09

暴力證明 Relational Fold-Fusion

我想這個證明是一定要重寫的,所以我就不花時間多做解釋了 XD。基本上就是把 scm 老師給的證明很暴力地轉成 Agda。

首先是把 relational fold 重整成 functional fold。這個部份還好,用 的 reflexivity 就過去了。

fun-to-rel : {A B C : Set} -> 
  (R : C ← B) (S : B ← (A × B)) (T : C ← (A × C)) (U : ℙ B) (V : ℙ C) ->
  ∀ (\x -> (foldr (Λ₁ (T ○₁ (idR ⨉₁ ∈))) V x)
         ⊆ (Λ₁ (R ○₁ ∈) (foldr (Λ₁ (S ○₁ (idR ⨉₁ ∈))) U x))) ->
  (R ○ fold S U) ⊇ fold T V
fun-to-rel R S T U V ⊆-pf x =
  chain> Λ(fold T V) x
       ⊂ foldr (Λ₁ (T ○₁ (idR ⨉₁ ∈))) V x by refl-⊆
       ⊂ Λ₁(R ○₁ ∈) (foldr (Λ₁ (S ○₁ (idR ⨉₁ ∈))) U x) by ⊆-pf x
       ⊂ Λ₁(R ○₁ ∈) (Λ (fold S U) x) by refl-⊆
       ⊂ (Λ₁(R ○₁ ∈) ₁○ Λ (fold S U)) x by refl-⊆
       ⊂ Λ(R ○ fold S U) x by refl-⊆

接下來是在 funciontal fold 上證明 fusion。那些 lemmas 都是暴力產生的 XD。

fusion : {A B C : Set} ->
  (R : C ← B) (S : B ← (A × B)) (T : C ← (A × C)) (U : ℙ B) (V : ℙ C) ->
  (R ○ S) ⊇ (T ○ (idR ⨉ R)) -> V ⊆ Λ₁ (R ○₁ ∈) U ->
  ∀ (\x -> (foldr (Λ₁ (T ○₁ (idR ⨉₁ ∈))) V x)
         ⊆ (Λ₁ (R ○₁ ∈) (foldr (Λ₁ (S ○₁ (idR ⨉₁ ∈))) U x)))
fusion R S T U V step-cond base-cond [] =
  chain> (foldr (Λ₁ (T ○₁ (idR ⨉₁ ∈))) V [])
       ⊂ V by refl-⊆
       ⊂ Λ₁ (R ○₁ ∈) U by base-cond
fusion R S T U V step-cond base-cond (a :: x) =
  chain> foldr (Λ₁ (T ○₁ (idR ⨉₁ ∈))) V (a :: x)
       ⊂ Λ₁ (T ○₁ (idR ⨉₁ ∈)) < a , foldr (Λ₁(T ○₁ (idR ⨉₁ ∈))) V x >₁
           by refl-⊆
       ⊂ Λ₁ (T ○₁ (idR ⨉₁ ∈))
            < a , Λ₁(R ○₁ ∈) (foldr (Λ₁(S ○₁ (idR ⨉₁ ∈))) U x) >₁
           by lemma1 T (fusion R S T U V step-cond base-cond x) a
       ⊂ Λ₁ (T ○₁ (idR ⨉₁ ∈))
            (〈 id , Λ₁ (R ○₁ ∈) 〉₁ < a , foldr (Λ₁(S ○₁ (idR ⨉₁ ∈))) U x >₁)
           by lemma2 (Λ₁ (T ○₁ (idR ⨉₁ ∈))) R a
                     (foldr (Λ₁(S ○₁ (idR ⨉₁ ∈))) U x)
       ⊂ Λ₁ (T ○₁ (idR ⨉₁ (R ○₁ ∈)))
           < a , foldr (Λ₁(S ○₁ (idR ⨉₁ ∈))) U x >₁
           by lemma3 T (R ○₁ ∈) a (foldr (Λ₁(S ○₁ (idR ⨉₁ ∈))) U x)
       ⊂ Λ₁ ((T ○ (idR ⨉ R)) ○₁ (idR ⨉₁ ∈))
           < a , foldr (Λ₁(S ○₁ (idR ⨉₁ ∈))) U x >₁
           by lemma4 R T a (foldr (Λ₁(S ○₁ (idR ⨉₁ ∈))) U x)
       ⊂ Λ₁ ((R ○ S) ○₁ (idR ⨉₁ ∈))
           < a , foldr (Λ₁(S ○₁ (idR ⨉₁ ∈))) U x >₁
           by lemma5 R S T (idR ⨉₁ ∈) step-cond a
                     (foldr (Λ₁(S ○₁ (idR ⨉₁ ∈))) U x)
       ⊂ Λ₁ (R ○₁ ∈) (Λ₁ (S ○₁ (idR ⨉₁ ∈))
            < a , foldr (Λ₁(S ○₁ (idR ⨉₁ ∈))) U x >₁)
           by lemma6 R S (idR ⨉₁ ∈) a (foldr (Λ₁(S ○₁ (idR ⨉₁ ∈))) U x)
       ⊂ Λ₁ (R ○₁ ∈) (foldr (Λ₁ (S ○₁ (idR ⨉₁ ∈))) U (a :: x))
           by refl-⊆
  where
    lemma1 : {A B C : Set} {s t : ℙ B} (T : C ← (A × B)) -> s ⊆ t ->
               ∀ (\a -> Λ₁ (T ○₁ (idR ⨉₁ ∈)) < a , s >₁
                      ⊆ Λ₁ (T ○₁ (idR ⨉₁ ∈)) < a , t >₁)
    lemma1 _ s⊆t a x (∃-I < a' , b' > (/\-I (/\-I a≡a' sb') T<a',b'>x))=
      ∃-I < a' , b' > (/\-I (/\-I a≡a' (s⊆t b' sb')) T<a',b'>x)

    lemma2 : {A B C : Set} (F : (A ×₁ ℙ C) -> ℙ C) (R : C ← B) ->
               (a : A) -> (bs : ℙ B) ->
               F < a , Λ₁(R ○₁ ∈) bs >₁ ⊆ F (〈 id , Λ₁ (R ○₁ ∈) 〉₁ < a , bs >₁)
    lemma2 _ _ _ _ _ pf = pf

    lemma3 : {A B C : Set} (T : C ← (A × C)) (X : C ←₁ ℙ B) ->
               (a : A) -> (bs : ℙ B) ->
               Λ₁(T ○₁ (idR ⨉₁ ∈)) (〈 id , Λ₁ X 〉₁ < a , bs >₁)
             ⊆ Λ₁(T ○₁ (idR ⨉₁ X)) < a , bs >₁
    lemma3 T X a bs c (∃-I < a' , c' > pf) = ∃-I < a' , c' > pf

    lemma4 : {A B C : Set} (R : C ← B) (T : C ← (A × C)) ->
               (a : A) (bs : ℙ B) ->
               Λ₁ (T ○₁ (idR ⨉₁ (R ○₁ ∈))) < a , bs >₁
             ⊆ Λ₁ ((T ○ (idR ⨉ R)) ○₁ (idR ⨉₁ ∈)) < a , bs >₁
    lemma4 _ _ _ _ _
           (∃-I < a' , c' > (/\-I (/\-I pf1 (∃-I b' (/\-I pf2 pf3))) pf4)) =
      ∃-I < a' , b' >
          (/\-I (/\-I pf1 pf2) (∃-I < a' , c' > (/\-I (/\-I refl pf3) pf4)))

    lemma5 : {A B C : Set} (R : C ← B) (S : B ← (A × B)) 
                           (T : C ← (A × C)) (X : (A × B) ←₁ (A ×₁ ℙ B)) ->
               (R ○ S) ⊇ (T ○ (idR ⨉ R)) -> (a : A) -> (bs : ℙ B) ->
               Λ₁ ((T ○ (idR ⨉ R)) ○₁ X) < a , bs >₁
             ⊆ Λ₁ ((R ○ S) ○₁ X) < a , bs >₁
    lemma5 _ _ _ _ pf1 _ _ c (∃-I < a' , b' > (/\-I pf2 pf3)) =
      ∃-I < a' , b' > (/\-I pf2 (pf1 < a' , b' > c pf3))

    lemma6 : {A B C : Set}
               (R : C ← B) (S : B ← (A × B)) (X : (A × B) ←₁ (A ×₁ ℙ B)) ->
               (a : A) -> (bs : ℙ B) ->
               Λ₁ ((R ○ S) ○₁ X) < a , bs >₁
             ⊆ Λ₁ (R ○₁ ∈) (Λ₁ (S ○₁ X) < a , bs >₁)
    lemma6 _ _ _ _ _ c (∃-I < a , b > (/\-I pf1 (∃-I b' (/\-I pf2 pf3)))) =
      ∃-I b' (/\-I (∃-I < a , b > (/\-I pf1 pf2)) pf3)

最後把上面兩個證明組合起來,就得到整個 relational fold-fusion theorem。

rel-fold-fusion : {A B C : Set} ->
  (R : C ← B) (S : B ← (A × B)) (T : C ← (A × C)) (U : ℙ B) (V : ℙ C) ->
  (R ○ S) ⊇ (T ○ (idR ⨉ R)) -> V ⊆ Λ₁ (R ○₁ ∈) U -> (R ○ fold S U) ⊇ fold T V
rel-fold-fusion R S T U V step-cond base-cond =
  fun-to-rel R S T U V (fusion R S T U V step-cond base-cond)

--
真是太暴力了 XD。

Labels:

2007/12/08

吐舌

Relational derivation 好難!XD 現在 relational fold-fusion theorem 已經證到 inductive case,可是這才是最麻煩的部份,舉步維艱。好像要證好多好多好複雜的 lemma,還要定義更多的 composition XD。然後在做 equality reasoning 的時候可以很輕鬆對局部做代換,現在都不行了(因為變成 "inequality"),痛苦啊 XD。

--
現在 inductive case 實質上只證了一步 XD。


第二步證明中,我在錯誤訊息裡面看到 Set2 了!XD

--
Set2 耶 XD。


喔耶,第二步成功了!XD

Labels:

2007/11/13

Transitivity Reprise

我證不出 permutation 的 transitivity!也就是說,如果 xs 和 ys 是彼此的 permutation,ys 和 zs 又是彼此的 permutation,那麼 xs 和 zs 也是彼此的 permutation(xs, ys, zs 都是 lists)。我用的「互為 permutation」的定義是

data IsPermutation {A : Set} : List A -> List A -> Set where
  perm-base : IsPermutation [] []
  perm-step : {x : A}{xs : List A}(ys zs : List A) ->
                IsPermutation xs (ys ++ zs) ->
                IsPermutation (x :: xs) (ys ++ x :: zs)

憑直覺照著 Agda 給的 context, goal type 那些互動資訊走的話,很容易導致 infinite recursion。只要這一步寫完,qsort 的正確性就完全證明了。

--
怎麼都是 transitivity…頭好痛 XD。

Labels:

QSort in Agda, Round 4

This round we externally prove that qsort returns an increasingly-sorted list. The logic is quite the same, so I'll simply post the code without explanation. A "philosophical" argument follows the code.

partition-bdd : (p : Nat)(xs : List Nat) -> {m n : Nat} ->
                  (bddAbove m ⋯ bddBelow n) xs ->
                  ((\pair -> (bddAbove p ⋯ bddBelow n) (fst pair)) ⋯
                   (\pair -> (bddAbove m ⋯ bddBelow p) (snd pair)))
                  (partition p xs)
partition-bdd p [] {m}{n} _ = (bdd-above-base {p} ‖ bdd-below-base {n}) ‖
                              (bdd-above-base {m} ‖ bdd-below-base {p})
partition-bdd p (x :: xs) {m}{n}
  (bdd-above-step xs-bddabv x≼m ‖ bdd-below-step xs-bddblw n≼x)
  with partition p xs
     | partition-bdd p xs {m}{n} (xs-bddabv ‖ xs-bddblw)
     | cmp x p
... | < ys , zs > | (ys-bddabv ‖ ys-bddblw) ‖ zs-bddpfs | \/-IL x≼p =
  (bdd-above-step ys-bddabv x≼p ‖ bdd-below-step ys-bddblw n≼x) ‖ zs-bddpfs
... | < ys , zs > | ys-bddpfs ‖ (zs-bddabv ‖ zs-bddblw) | \/-IR p≼x =
  ys-bddpfs ‖ (bdd-above-step zs-bddabv x≼m ‖ bdd-below-step zs-bddblw p≼x)

qsort-inc-sorted : ∀ (\xs -> {m n : Nat} ->(bddAbove m ⋯ bddBelow n) xs ->
                     (IncSorted ⋯ bddAbove m ⋯ bddBelow n) (qsort xs))
qsort-inc-sorted [] bddpf = inc-sorted-base ‖ bddpf
qsort-inc-sorted (x :: xs) {m}{n}
  (bdd-above-step xs-bddabv x≼m ‖ bdd-below-step xs-bddblw n≼x)
  with partition x xs | partition-bdd x xs (xs-bddabv ‖ xs-bddblw)
... | < ys , zs > | ys-bdd ‖ zs-bdd
  with qsort-inc-sorted ys ys-bdd | qsort-inc-sorted zs zs-bdd
qsort-inc-sorted (x :: xs) {m}{n}
  (bdd-above-step xs-bddabv x≼m ‖ bdd-below-step xs-bddblw n≼x)
  | < ys , zs > | ys-bdd ‖ zs-bdd | ys'-sorted ‖ ys'-bddabv ‖ ys'-bddblw
                                  | zs'-sorted ‖ zs'-bddabv ‖ zs'-bddblw =
  inc-sorted-concat
    ys'-sorted
    (inc-sorted-step zs'-sorted zs'-bddblw (≼-refl {x}))
    ys'-bddabv
    (bdd-below-step zs'-bddblw (≼-refl {x})) ‖
  bdd-above-concat (relaxUpperBound ys'-bddabv x≼m)
                   (bdd-above-step zs'-bddabv x≼m) ‖
  bdd-below-concat ys'-bddblw
    (relaxLowerBound (bdd-below-step zs'-bddblw (≼-refl {x})) n≼x)

I think it is appropriate to name this kind of proof style externalism, while the intrusive approach taken in round 1 and 2 may be named internalism. The two terms are borrowed from Epistemology, where internalism requires one to provide justification for his/her (true) belief in order to claim that belief is knowledge, while externalism doesn't impose such a requirement but instead allows a justification to be provided externally by someone else.

So far my experience shows that externalism naturally leads to modularized proofs and (at least) won't take more effort than internalism, since Agda supports externalism reasonably well (e.g., magic with), while internalist proofs are coupled with algorithms or data structures and thus hard to combine or reuse. Besides, if one really needs an internalist version, he/she can simply write something like

qsort''′ : List Nat -> List Nat ∣ IncSorted
qsort''′ xs with lemma3 xs
qsort''′ xs | ∃-I _ (xs' ⋮ xs≡xs' ‖ xs'-bdd)
  with qsort-inc-sorted xs' xs'-bdd
qsort''′ xs | ∃-I _ (xs' ⋮ xs≡xs' ‖ xs'-bdd) | xs''-sorted ‖ _ =
  qsort xs ⋮ subst (\ys -> IncSorted (qsort ys)) xs≡xs' xs''-sorted

So currently I think externalism has the advantage over internalism. (Just contrary to the position I take toward epistemic internalism and externalism!)

--
The next (and, very likely, the final) round would be proving the ultimate correctness of qsort!


I think I'm going to stop here since I just can't prove that the relation IsPermutation xs ys is transitive... Anyway, the primary aim of this series of exercises has been achieved, and that's good. XD

Labels: ,

2007/11/12

QSort in Agda, Round 3

After figuring out how to use the magic with, the rest is quite straightforward --- the logic is identical to the one used to prove the intrusive version.

qsort-lenpres : ∀ (\xs -> EqualLength xs (qsort xs))
qsort-lenpres [] = eqlen-base
qsort-lenpres (x :: xs) with partition x xs | partition-lenpres xs x
qsort-lenpres (x :: xs) | < ys , zs > | ind-hyp₁ with qsort-lenpres ys
                                                   | qsort-lenpres zs
qsort-lenpres (x :: xs) | < ys , zs > | ind-hyp₁ | ind-hyp₂ | ind-hyp₃ =
  chainˡ> x :: xs
     ===ˡ qsort ys ++ x :: qsort zs byˡ lemma1 x x xs (qsort ys) (qsort zs)
          (chainˡ> xs
              ===ˡ ys ++ zs byˡ ind-hyp₁
              ===ˡ qsort ys ++ qsort zs byˡ lemma2 ind-hyp₂ ind-hyp₃)

It seems that one has to with-match partition x xs and partition-lenpres xs x simultaneously in order to make Agda know that partition x xs is indeed < ys , zs > when it is inferring the type of ind-hyp₁.

--
I really have to go to bed now since I've got a midterm in the morning. XD

Labels: ,

A Question about Magic With --- Resolved

While I was on the bed listening to Eric Clapton's Bell Bottom Blues, it occurred to me that I could probably use subst to replace partition p xs with < ys , zs >, i.e., something like

subst (\X -> EqualLength xs (pair-concat X))
      (refl {< ys , zs >}) (partition xs p)

I expect there are some mysterious connection between a term and a pattern that is matched with the term, which would remind Agda that partition p xs is in fact < ys , zs >. Unfortunately it failed. But not much later I came up with the idea that maybe doing a direct with-match on partition-lenpres xs p would work, and indeed it worked! The final result is

partition-lenpres : (xs : List Nat)(p : Nat) ->
                      EqualLength xs (pair-concat (partition p xs))
partition-lenpres [] _ = eqlen-base
partition-lenpres (x :: xs) p with partition p xs | cmp x p
                                 | partition-lenpres xs p  -- magic with
... | < ys , zs > | \/-IL x≼p | indHyp = eqlen-step indHyp
... | < ys , zs > | \/-IR p≼x | indHyp = lemma1 x x xs ys zs indHyp

where lemma1 has been proved when I was working on the "intrusive" proof.

lemma1 : {A : Set}(x z : A)(xs ys zs : List A) ->
          EqualLength xs (ys ++ zs) -> EqualLength (x :: xs) (ys ++ z :: zs)

--
Excellent! Though I still don't understand exactly how with works...

Labels: ,