General properties of well-quasiorders.

module WQO where

import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; refl; _≢_; sym; trans; subst; cong; _≗_)
open Eq.≡-Reasoning
open import Data.Nat using (ℕ; zero; suc; _+_; _≤_; s≤s; z≤n; _<_; _⊔_)
open import Data.Nat.Properties
  using (≤-refl; ≤-trans; <-trans; <-irrefl; m≤n+m; +-monoˡ-<; <-transʳ; <-transˡ; m≤m⊔n; m≤n⊔m; +-assoc; m≤n⇒m≤o+n; m≤n⇒m≤n+o)
open import Data.Product using (Σ; _×_; _,_; proj₁; proj₂ ; ∃; Σ-syntax; ∃-syntax; ∃₂)
open import Data.Sum using (_⊎_; inj₁; inj₂) renaming ([_,_]′ to case-⊎)
open import Relation.Nullary using (¬_)
open import Data.Unit using (⊤; tt)
open import Data.Empty using (⊥; ⊥-elim)
open import Relation.Unary using (_∈_; _⊆_; Satisfiable)
open import Function using (_∘_; id)
open import Data.Maybe using (Maybe; just; nothing)

open import Miscellaneous

open import Classical

private
  variable
    A B : Set
Definitions of quasiorder and well-quasiorder.
Reflexive : (A → A → Set) → Set
Reflexive _≼_ = ∀ {x} → x ≼ x

Transitive : (A → A → Set) → Set
Transitive _≼_ = ∀ {x y z} → x ≼ y → y ≼ z → x ≼ z

record IsQO (_≼_ : A → A → Set) : Set where
  field
    reflexive : Reflexive _≼_ -- (x : A) → x ≼ x
    transitive : Transitive _≼_ -- {x y z : A} → x ≼ y → y ≼ z → x ≼ z

open IsQO

Good : (_≼_ : A → A → Set) → (ℕ → A) → Set
Good _≼_ f = ∃₂ (λ i j → i < j × f i ≼ f j)

good-extensional : {_≼_ : A → A → Set} (f g : ℕ → A) → ((n : ℕ) → f n ≡ g n) → 
                   Good _≼_ f → Good _≼_ g
good-extensional {A} {_≼_} f g f≗g (i , j , i<j , f[i]≼f[j]) = i , j , i<j , g[i]≼g[j]
  where
  g[i]≼g[j] : g i ≼ g j
  g[i]≼g[j] rewrite sym (f≗g i) | sym (f≗g j) = f[i]≼f[j]

A sequence is very good if every subsequence of it is good.
VeryGood : (_≼_ : A → A → Set) → (ℕ → A) → Set
VeryGood _≼_ f = (r : ℕ → ℕ) → Increasing _<_ r → Good _≼_ (f ∘ r)

veryGood-extensional : {_≼_ : A → A → Set} (f g : ℕ → A)
  → ((n : ℕ) → f n ≡ g n)
  → VeryGood _≼_ f
  ----------------
  → VeryGood _≼_ g
veryGood-extensional {A} {_≼_} f g f≗g f-veryGood r r-increasing = i , j , i<j , g∘r[i]≼g∘r[j]
  where
  h : ∃₂ (λ i j → i < j × f (r i) ≼ f (r j))
  h = f-veryGood r r-increasing
  i = proj₁ h
  j = proj₁ (proj₂ h)
  i<j = proj₁ (proj₂ (proj₂ h))
  f∘r[i]≼f∘r[j] : f (r i) ≼ f (r j)
  f∘r[i]≼f∘r[j] = proj₂ (proj₂ (proj₂ h))
  g∘r[i]≼g∘r[j] : g (r i) ≼ g (r j)
  g∘r[i]≼g∘r[j] = subst (λ z → z ≼ g (r j)) (f≗g (r i)) (subst (λ z → f (r i) ≼ z) (f≗g (r j)) f∘r[i]≼f∘r[j])

veryGood-mono : (_≼₁_ _≼₂_ : A → A → Set) (f : ℕ → A) →
  ((x y : A) → x ≼₁ y → x ≼₂ y) →
  VeryGood _≼₁_ f → VeryGood _≼₂_ f
veryGood-mono _≼₁_ _≼₂_ f h₁ h₂ r r-increasing = i , j , i<j , f∘r[i]≼₂f∘r[j] 
  where
  [1] : Good _≼₁_ (f ∘ r)
  [1] = h₂ r r-increasing
  i j : ℕ
  i = proj₁ [1]
  j = proj₁ (proj₂ [1])
  i<j : i < j
  i<j = proj₁ (proj₂ (proj₂ [1]))
  f∘r[i]≼₁f∘r[j] : f (r i) ≼₁ f (r j)
  f∘r[i]≼₁f∘r[j] = proj₂ (proj₂ (proj₂ [1]))
  f∘r[i]≼₂f∘r[j] : f (r i) ≼₂ f (r j)
  f∘r[i]≼₂f∘r[j] = h₁ (f (r i)) (f (r j)) f∘r[i]≼₁f∘r[j]


veryGood⇒good : (_≼_ : A → A → Set) (f : ℕ → A) → VeryGood _≼_ f → Good _≼_ f
veryGood⇒good _≼_ f f-veryGood = f-veryGood id id-increasing

subseq-veryGood : (_≼_ : A → A → Set) (f : ℕ → A) (r : ℕ → ℕ)
  → VeryGood _≼_ f
  → Increasing _<_ r
  ----------------------
  → VeryGood _≼_ (f ∘ r)
subseq-veryGood _≼_ f r f-veryGood r-increasing r₁ r₁-increasing
  = f-veryGood (r ∘ r₁) r∘r₁-increasing
  where
  r∘r₁-increasing : Increasing _<_ (r ∘ r₁)
  r∘r₁-increasing = subseq-increasing _<_ r r₁ r-increasing r₁-increasing
The following form of reasoning is used repeatedly, but it’s often cumbersome to identify P and prove that it’s closed under subsequence, so I usually won’t bother invoking this lemma.
ClosedUnderSubseq : (P : (ℕ → A) → Set) → Set
ClosedUnderSubseq {A} P = (f : ℕ → A) (r : ℕ → ℕ) → Increasing _<_ r → P f → P (f ∘ r)

lemma-good-veryGood : {_≼_ : A → A → Set} (P : (ℕ → A) → Set)
  → ClosedUnderSubseq P
  → ((f : ℕ → A) → P f → Good _≼_ f)
  → (f : ℕ → A)
  → P f 
  → VeryGood _≼_ f
lemma-good-veryGood P P-closed h f Pf r r-increasing = h (f ∘ r) (P-closed f r r-increasing Pf)

AlmostFull : (_≼_ : A → A → Set) → Set
AlmostFull _≼_ = ∀ f → Good _≼_ f

almostFull-mon : (_≼₁_ _≼₂_ : A → A → Set)
  → ((x y : A) → (x ≼₁ y) → (x ≼₂ y))
  → AlmostFull _≼₁_
  → AlmostFull _≼₂_
almostFull-mon _≼₁_ _≼₂_ h ≼₁-almostFull f with ≼₁-almostFull f
... | i , j , i<j , fi≼₁fj = i , j , i<j , h (f i) (f j) fi≼₁fj

record IsWQO (_≼_ : A → A → Set) : Set where
  field
    isQO : IsQO _≼_
    almostFull : AlmostFull _≼_

open IsWQO
Some consequences.
module _ (_≼_ : A → A → Set) where

  module _ (f : ℕ → A) where

    almostFull-veryGood : AlmostFull _≼_ → VeryGood _≼_ f
    almostFull-veryGood h r r-increasing = h (f ∘ r)
An index m is a near-nonterminal if f m has only finitely many ≼-successors in the sequence f.
    NearTerminal : ℕ → Set
    NearTerminal m = Finite (λ n → f m ≼ f n)
      -- ∃[ k ] ((n : ℕ) → k < n → ¬ f m ≼ f n)

    NonNearTerminal : ℕ → Set
    NonNearTerminal m = Infinite (λ n → f m ≼ f n)
      -- (k : ℕ) → ∃[ n ] (k < n × f m ≼ f n)

    ¬NearTerminal⇒NonNearTerminal : (m : ℕ) → ¬ NearTerminal m → NonNearTerminal m
    ¬NearTerminal⇒NonNearTerminal m h = ¬finite⇒infinite (λ n → f m ≼ f n) h

    ¬NonNearTerminal⇒NearTerminal : (m : ℕ) → ¬ NonNearTerminal m →
      NearTerminal m
    ¬NonNearTerminal⇒NearTerminal m h = ¬infinite⇒finite (λ n → f m ≼ f n) h
Every very good sequence (w.r.t. ≼) has a near-nonterminal.
    veryGood⇒nonNearTerminal : VeryGood _≼_ f → Satisfiable NonNearTerminal
    veryGood⇒nonNearTerminal f-veryGood = ¬¬-elim [1]
      where
      [1] : ¬ (¬ Satisfiable NonNearTerminal)
      [1] h₁ = f∘r-bad f∘r-good
        where
        [2] : (n : ℕ) → NearTerminal n
        [2] n = ¬NonNearTerminal⇒NearTerminal n (¬∃⟶∀¬ h₁ n)

        r : ℕ → ℕ
        
        r zero    = zero
        r (suc n) = suc (k ⊔ r n)
          where
          [3] : ∃[ k ] ((m : ℕ) → k < m → ¬ f (r n) ≼ f m)
          [3] = [2] (r n)

          k = proj₁ [3]

        r-step : (n : ℕ) → r n < r (suc n)
        r-step n = s≤s (m≤n⊔m _ (r n))

        r-increasing : Increasing _<_ r
        r-increasing = step-increasing r r-step

        r-spec : (n m : ℕ) → r (suc n) ≤ m → ¬ f (r n) ≼ f m
        r-spec n m r[1+n]≤m f∘r[n]≼f[m] = proj₂ [4] m k<m f∘r[n]≼f[m]
          where
          [4] : ∃[ k ] ((m : ℕ) → k < m → ¬ f (r n) ≼ f m)
          [4] = [2] (r n)

          k = proj₁ [4]

          k<m : k < m
          k<m = <-transˡ (s≤s (m≤m⊔n k (r n))) r[1+n]≤m

        f∘r-bad : ¬ Good _≼_ (f ∘ r)
        f∘r-bad (i , j , i<j , f∘r[i]≼f∘r[j]) = r-spec i (r j) r[1+i]≤r[j] f∘r[i]≼f∘r[j]
          where
          r[1+i]≤r[j] : r (suc i) ≤ r j
          r[1+i]≤r[j] = increasing-≤ r r-increasing (suc i) j i<j

        f∘r-good : Good _≼_ (f ∘ r)
        f∘r-good = f-veryGood r r-increasing
If the first term of a sequence is not a near-terminal, then that term is a lower bound (w.r.t. ≼) of a subsequence of that sequence.
    nonNearTerminal⇒LowerBoundSubseq :
      NonNearTerminal zero → 
      Σ[ r ∈ (ℕ → ℕ) ] (Increasing _<_ r × zero < r zero × All (f zero ≼_) (f ∘ r))
    nonNearTerminal⇒LowerBoundSubseq h = r , r-increasing , [1] , [2]
      where
      r : ℕ → ℕ
      r zero    = proj₁ (h zero)
      r (suc n) = proj₁ (h (r n))

      r-step : (n : ℕ) → r n < r (suc n)
      r-step n = proj₁ (proj₂ (h (r n)))

      r-increasing : Increasing _<_ r
      r-increasing = step-increasing r r-step

      [1] : zero < r zero
      [1] = proj₁ (proj₂ (h zero))

      [2] : (n : ℕ) → f zero ≼ f (r n)
      [2] zero = proj₂ (proj₂ (h zero))
      [2] (suc n) = proj₂ (proj₂ (h (r n)))

  nonNearTerminal-tail : (f : ℕ → A) (x : ℕ) (y : ℕ) →
    NonNearTerminal f (x + y) → NonNearTerminal (f ∘ (_+ y)) x
  nonNearTerminal-tail f x y h k = 
    o + suc k , m≤n⇒m≤o+n o ≤-refl , subst (λ z → f (x + y) ≼ f z) [2] (proj₂ (proj₂ (h (k + y))))
    where
    -- h (k + y) : ∃[ l ] (k + y < l × f (x + y) ≼ f l)

    l = proj₁ (h (k + y))

    k+y<l : k + y < l
    k+y<l = proj₁ (proj₂ (h (k + y)))

    [1] : ∃[ o ] o + (suc k + y) ≡ l
    [1] = m≤n⇒∃[o]o+m≡n k+y<l

    o = proj₁ [1]

    [2] : l ≡ (o + suc k) + y
    [2] = sym (trans (+-assoc o (suc k) y) (proj₂ [1]))
We can now prove that every very good sequence has an ascending subsequence.
  veryGood⇒ascendingSubseq : 
    (f : ℕ → A) → VeryGood _≼_ f →
    Σ[ r ∈ (ℕ → ℕ) ] (Increasing _<_ r × Increasing _≼_ (f ∘ r))
  veryGood⇒ascendingSubseq f f-veryGood = r , r-increasing , [14]
    where
    [1] : Satisfiable (NonNearTerminal f)
    [1] = veryGood⇒nonNearTerminal f f-veryGood

    n₀ = proj₁ [1]

    n₀-spec : NonNearTerminal f n₀
    n₀-spec = proj₂ [1]

    f′ : ℕ → A
    f′ n = f (n + n₀)

    +n₀-increasing : Increasing _<_ (_+ n₀)
    +n₀-increasing m n m<n = +-monoˡ-< n₀ m<n

    nonNearTerminal-f′ : NonNearTerminal f′ zero
    nonNearTerminal-f′ = nonNearTerminal-tail f zero n₀ n₀-spec

    f′-veryGood : VeryGood _≼_ f′
    f′-veryGood = subseq-veryGood _≼_ f (_+ n₀) f-veryGood +n₀-increasing

We’re going to construct an infinite sequence of subsequences of f:

f ∘ (r′ 0) , f ∘ (r′ 1) , f ∘ (r′ 2) , ...

For each j, f ∘ (r′ (suc j)) is a subsequence of f ∘ (r′ j), i.e., there is an increasing sequence s′ j such that r′ (suc j) equals r′ j ∘ s′ j. This makes each r′ j an increasing function, so that f ∘ (r′ j) is a subsequence of f and is hence very good. Moreover, r′ j zero has the following properties:

I introduce all necessary functions and properties by mutually recursive definitions. This is a little too complicated, and I definitely need to clean it up.
    r′ : ℕ → ℕ → ℕ
    r′-spec₁ : (j : ℕ) → Increasing _<_ (r′ j)
    r′-spec₂ : (j : ℕ) → NonNearTerminal (f ∘ r′ j) zero
    r′-spec₃ : (j : ℕ) → r′ j zero < r′ (suc j) zero
    r′-spec₄ : (j n : ℕ) → f (r′ j zero) ≼ f (r′ (suc j) n)
    s : ℕ → ℕ → ℕ
    s-spec₁ : (j : ℕ) → Increasing _<_ (s j)
    s-spec₂ : (j : ℕ) → zero < s j zero
    s-spec₃ : (j : ℕ) → All (f (r′ j zero) ≼_) (f ∘ r′ j ∘ s j)
    s-spec₄ : (j : ℕ) → Satisfiable (NonNearTerminal (f ∘ (r′ j ∘ s j)))

    r′-helper : 
      (j : ℕ) → Σ[ s ∈ (ℕ → ℕ) ] (Increasing _<_ s × zero < s zero × 
                                  All (f (r′ j zero) ≼_) (f ∘ r′ j ∘ s))

    s′ : ℕ → ℕ → ℕ
    s′-increasing : (j : ℕ) → Increasing _<_ (s′ j)

    r′ zero = _+ n₀
    r′ (suc j) = r′ j ∘ s j ∘ (_+ n)
      where
      n = proj₁ (s-spec₄ j)

    r′-spec₁ zero m n m<n = +-monoˡ-< n₀ m<n
    r′-spec₁ (suc j) = 
      compose-increasing (compose-increasing (λ o p o<p → +-monoˡ-< n o<p) (s-spec₁ j)) (r′-spec₁ j)
      where
      n = proj₁ (s-spec₄ j)

    r′-spec₂ zero    = nonNearTerminal-f′
    r′-spec₂ (suc j) = nonNearTerminal-tail (f ∘ (r′ j ∘ s j)) zero n n-spec
      where
      n = proj₁ (s-spec₄ j)
      n-spec : NonNearTerminal (f ∘ (r′ j ∘ s j)) n
      n-spec = proj₂ (s-spec₄ j)

    r′-spec₃ j = <-transˡ (r′-spec₁ j zero (s j zero) (s-spec₂ j)) (r′[j]∘s[j]-nondecreasing zero n z≤n)
      where
      n = proj₁ (s-spec₄ j)
      r′[j]∘s[j]-increasing : Increasing _<_ (r′ j ∘ s j)
      r′[j]∘s[j]-increasing = compose-increasing (s-spec₁ j) (r′-spec₁ j)
      r′[j]∘s[j]-nondecreasing : (o p : ℕ) → o ≤ p → (r′ j (s j o) ≤ r′ j (s j p))
      r′[j]∘s[j]-nondecreasing = increasing-≤ (r′ j ∘ s j) r′[j]∘s[j]-increasing

    r′-spec₄ j = all-compose {P = f (r′ j zero) ≼_} (_+ n) (s-spec₃ j)
      where
      n = proj₁ (s-spec₄ j)

    r′-helper j = nonNearTerminal⇒LowerBoundSubseq (f ∘ r′ j) (r′-spec₂ j)

    s j = proj₁ (r′-helper j)

    s-spec₁ j = proj₁ (proj₂ (r′-helper j))

    s-spec₂ j = proj₁ (proj₂ (proj₂ (r′-helper j)))

    s-spec₃ j = proj₂ (proj₂ (proj₂ (r′-helper j)))

    s-spec₄ j = veryGood⇒nonNearTerminal (f ∘ r′ j ∘ s j) f∘r′[j]∘s[j]-veryGood
      where
      r′[j]∘s[j]-increasing : Increasing _<_ (r′ j ∘ s j)
      r′[j]∘s[j]-increasing = compose-increasing (s-spec₁ j) (r′-spec₁ j)
      f∘r′[j]∘s[j]-veryGood : VeryGood _≼_ (f ∘ r′ j ∘ s j)
      f∘r′[j]∘s[j]-veryGood = subseq-veryGood _≼_ f (r′ j ∘ s j) f-veryGood r′[j]∘s[j]-increasing

    s′ j = s j ∘ (_+ proj₁ (s-spec₄ j))

    s′-increasing j = compose-increasing (λ o p o<p → +-monoˡ-< n o<p) (s-spec₁ j)
      where
      n = proj₁ (s-spec₄ j)

    [2] : (j : ℕ) → r′ j ∘ s′ j ≡ r′ (suc j)
    [2] j = refl

    [3] : (j p : ℕ) → Σ[ t ∈ (ℕ → ℕ) ] (Increasing _<_ t × r′ j ∘ t ≗ r′ (p + j))
    [3] j zero    = id , id-increasing , λ x → refl
    [3] j (suc p) = 
      t ∘ s′ (p + j) , 
      compose-increasing (s′-increasing (p + j)) t-increasing , 
      λ x → trans ([4] (s′ (p + j) x)) ([5] x)
      where
      t = proj₁ ([3] j p)
      t-increasing = proj₁ (proj₂ ([3] j p))
      [4] : r′ j ∘ t ≗ r′ (p + j)
      [4] = proj₂ (proj₂ ([3] j p))
      [5] : r′ (p + j) ∘ s′ (p + j) ≗ r′ (suc p + j)
      [5] x = subst (λ z → z x ≡ r′ (suc p + j) x) (sym ([2] (p + j))) refl

    [6] : (j p o : ℕ) → f (r′ j zero) ≼ f (r′ (p + suc j) o)
    [6] j p o = [13] o
      where
      t = proj₁ ([3] (suc j) p)
      [7] : r′ (suc j) ∘ t ≗ r′ (p + suc j)
      [7] = proj₂ (proj₂ ([3] (suc j) p))
      [8] : All (f (r′ j zero) ≼_) (f ∘ r′ (suc j))
      [8] = r′-spec₄ j
      [9] : All (f (r′ j zero) ≼_) ((f ∘ r′ (suc j)) ∘ t)
      [9] = all-compose {P = f (r′ j zero) ≼_} t [8]
      [10] : (f ∘ r′ (suc j)) ∘ t ≗ f ∘ (r′ (suc j) ∘ t)
      [10] = ∘-assoc t (r′ (suc j)) f
      [11] : All (f (r′ j zero) ≼_) (f ∘ (r′ (suc j) ∘ t))
      [11] = all-extensional {P = f (r′ j zero) ≼_} [10] [9]
      [12] : f ∘ (r′ (suc j) ∘ t) ≗ f ∘ r′ (p + suc j)
      [12] x = cong f ([7] x)
      [13] : All (f (r′ j zero) ≼_) (f ∘ r′ (p + suc j))
      [13] = all-extensional {P = f (r′ j zero) ≼_} [12] [11]

    r : ℕ → ℕ
    r i = r′ i zero

    [14] : (i j : ℕ) → i < j → f (r i) ≼ f (r j)
    [14] i j i<j = subst (λ z → f (r′ i zero) ≼ f (r′ z zero)) p+suc[i]≡j ([6] i p zero)
      where
      [15] : ∃[ p ] p + suc i ≡ j
      [15] = m<n⇒∃[o]o+suc[m]≡n i<j
      p = proj₁ [15]
      p+suc[i]≡j : p + suc i ≡ j
      p+suc[i]≡j = proj₂ [15]

    r-step : (i : ℕ) → r i < r (suc i)
    r-step i = r′-spec₃ i

    r-increasing : Increasing _<_ r
    r-increasing = step-increasing r r-step

  -- ascendingSubseq : IsWQO _≼_
  --   → Σ[ r ∈ (ℕ → ℕ) ] (Increasing _<_ r × Increasing _≼_ (f ∘ r))
  -- ascendingSubseq h =
  --   veryGood⇒ascendingSubseq (almostFull-veryGood (almostFull h))
Dickson’s lemma
prodSeq : (ℕ → A) → (ℕ → B) → (ℕ → A × B)
prodSeq f g n = f n , g n

prodOrder : (A → A → Set) → (B → B → Set) → A × B → A × B → Set
prodOrder _≼₁_ _≼₂_ x y = (proj₁ x ≼₁ proj₁ y) × (proj₂ x ≼₂ proj₂ y)

prod-reflexive : (_≼₁_ : A → A → Set) (_≼₂_ : B → B → Set)
  → Reflexive _≼₁_
  → Reflexive _≼₂_
  → Reflexive (prodOrder _≼₁_ _≼₂_)
prod-reflexive _≼₁_ _≼₂_ h₁ h₂ = h₁ , h₂

prod-transitive : (_≼₁_ : A → A → Set) (_≼₂_ : B → B → Set)
  → Transitive _≼₁_
  → Transitive _≼₂_
  → Transitive (prodOrder _≼₁_ _≼₂_)
prod-transitive _≼₁_ _≼₂_ h₁ h₂ xy yz = (h₁ (proj₁ xy) (proj₁ yz)) , h₂ (proj₂ xy) (proj₂ yz)

prod-isQO : (_≼₁_ : A → A → Set) (_≼₂_ : B → B → Set)
  → IsQO _≼₁_
  → IsQO _≼₂_
  ----------------------------
  → IsQO (prodOrder _≼₁_ _≼₂_)

prod-isQO _≼₁_ _≼₂_ h₁ h₂ = 
  record
    { reflexive = prod-reflexive _≼₁_ _≼₂_ (reflexive h₁) (reflexive h₂)
    ; transitive = prod-transitive _≼₁_ _≼₂_ (transitive h₁) (transitive h₂)
    }

prod-veryGood : {_≼₁_ : A → A → Set} {_≼₂_ : B → B → Set}
  {f : ℕ → A} {g : ℕ → B}
  → VeryGood _≼₁_ f
  → VeryGood _≼₂_ g
  ----------------------------------------------
  → VeryGood (prodOrder _≼₁_ _≼₂_) (prodSeq f g)
prod-veryGood {_≼₁_ = _≼₁_} {_≼₂_ = _≼₂_} {f = f} {g = g} 
  f-veryGood g-veryGood r r-increasing
  = r₁ i , r₁ j , r₁-increasing i j i<j , ( frr₁i≼₁frr₁j , grr₁i≼₂grr₁j )
  where
  _≼_ = prodOrder _≼₁_ _≼₂_
  f∘r-veryGood : VeryGood _≼₁_ (f ∘ r)
  f∘r-veryGood = subseq-veryGood _≼₁_ f r f-veryGood r-increasing
  f∘r-ascendingSubseq : 
    Σ[ r₁ ∈ (ℕ → ℕ) ] Increasing _<_ r₁ × Increasing _≼₁_ (f ∘ r ∘ r₁)
  f∘r-ascendingSubseq = 
    veryGood⇒ascendingSubseq _≼₁_ (f ∘ r)
    f∘r-veryGood
  r₁ : ℕ → ℕ
  r₁ = proj₁ f∘r-ascendingSubseq
  r₁-increasing : Increasing _<_ r₁
  r₁-increasing = proj₁ (proj₂ f∘r-ascendingSubseq)
  f∘r∘r₁-increasing : Increasing _≼₁_ (f ∘ r ∘ r₁)
  f∘r∘r₁-increasing = proj₂ (proj₂ f∘r-ascendingSubseq)
  r∘r₁-increasing : Increasing _<_ (r ∘ r₁)
  r∘r₁-increasing = subseq-increasing _<_ r r₁ r-increasing r₁-increasing
  g∘r∘r₁-good : Good _≼₂_ (g ∘ r ∘ r₁)
  g∘r∘r₁-good = g-veryGood (r ∘ r₁) r∘r₁-increasing
  i j : ℕ
  i = proj₁ g∘r∘r₁-good
  j = proj₁ (proj₂ g∘r∘r₁-good)
  i<j : i < j
  i<j = proj₁ (proj₂ (proj₂ g∘r∘r₁-good))
  grr₁i≼₂grr₁j : g (r (r₁ i)) ≼₂ g (r (r₁ j))
  grr₁i≼₂grr₁j = proj₂ (proj₂ (proj₂ g∘r∘r₁-good))
  frr₁i≼₁frr₁j : f (r (r₁ i)) ≼₁ f (r (r₁ j))
  frr₁i≼₁frr₁j = f∘r∘r₁-increasing i j i<j

prod-isWQO : (_≼₁_ : A → A → Set) (_≼₂_ : B → B → Set)
  → IsWQO _≼₁_
  → IsWQO _≼₂_
  -------------------------------------
  → IsWQO {A × B} (prodOrder _≼₁_ _≼₂_)
prod-isWQO {A = A} {B = B} _≼₁_ _≼₂_ h₁ h₂ = 
  record
    { isQO = prod-isQO _≼₁_ _≼₂_ (isQO h₁) (isQO h₂)
    ; almostFull = ≼-almostFull
    }
  where
  _≼_ = prodOrder _≼₁_ _≼₂_
  ≼-almostFull : (f : ℕ → A × B) → Good _≼_ f
  ≼-almostFull f = f₁f₂-good
    where
    f₁ : ℕ → A
    f₁ n = proj₁ (f n)
    f₂ : ℕ → B
    f₂ n = proj₂ (f n)
    f₁-veryGood : VeryGood _≼₁_ f₁
    f₁-veryGood = almostFull-veryGood _≼₁_ f₁ (almostFull h₁)
    f₂-veryGood : VeryGood _≼₂_ f₂
    f₂-veryGood = almostFull-veryGood _≼₂_ f₂ (almostFull h₂)
    f₁f₂-veryGood : VeryGood (prodOrder _≼₁_ _≼₂_) (prodSeq f₁ f₂)
    f₁f₂-veryGood = 
      prod-veryGood {_≼₁_ = _≼₁_} {_≼₂_ = _≼₂_} {f = f₁} {g = f₂}
        -- (transitive (isQO h₁))
      f₁-veryGood f₂-veryGood
    f₁f₂-good : Good (prodOrder _≼₁_ _≼₂_) (prodSeq f₁ f₂)
    f₁f₂-good = veryGood⇒good (prodOrder _≼₁_ _≼₂_) (prodSeq f₁ f₂) f₁f₂-veryGood
Maybe: maybe we should prove a more general lemma about unions?
module M (_≼_ : A → A → Set) where
  _≼′_ : Maybe A → Maybe A → Set
  just x  ≼′ just y  = x ≼ y
  just x  ≼′ nothing = ⊥
  nothing ≼′ just y  = ⊥
  nothing ≼′ nothing = ⊤ -- ??

  maybe-almostFull : AlmostFull _≼_ → AlmostFull _≼′_
  maybe-almostFull ≼-almostFull f = f-good
    where
    Inf = (k : ℕ) → Σ[ l ∈ ℕ ] k < l × f l ≡ nothing
    Cof = Σ[ k ∈ ℕ ] ((l : ℕ) → k < l → f l ≢ nothing)
    h₁ : Inf ⊎ Cof
    h₁ = infinite∨finite (λ z → f z ≡ nothing)
    f-good : Good _≼′_ f
    f-good with h₁
    ... | inj₁ inf = i , j , i<j , fi≼′fj
      where
      i j : ℕ
      i = proj₁ (inf zero)
      j = proj₁ (inf i) 
      i<j : i < j
      i<j = proj₁ (proj₂ (inf i))
      fi≡nothing : f i ≡ nothing
      fi≡nothing = proj₂ (proj₂ (inf zero))
      fj≡nothing : f j ≡ nothing
      fj≡nothing = proj₂ (proj₂ (inf i))
      fi≼′fj : f i ≼′ f j
      fi≼′fj rewrite fi≡nothing | fj≡nothing = tt
    ... | inj₂ cof = i + suc k , j + suc k , i+1+k<j+1+k , fi+1+k≼′fj+1+k
      where
      k = proj₁ cof
      cof′ : (l : ℕ) → k < l → ∃[ x ] f l ≡ just x
      cof′ l k<l with f l in eq
      ... | just x = x , refl
      ... | nothing = ⊥-elim (proj₂ cof l k<l eq)
      g : (n : ℕ) → ∃[ x ] f (n + suc k) ≡ just x
      g n = cof′ (n + suc k) (m≤n+m (suc k) n)
      f′ : ℕ → A
      f′ n = proj₁ (g n)
      f-f′ : (n : ℕ) → f (n + suc k) ≡ just (f′ n)
      f-f′ n = proj₂ (g n)
      f′-good : Good _≼_ f′
      f′-good = ≼-almostFull f′
      i j : ℕ
      i = proj₁ f′-good
      j = proj₁ (proj₂ f′-good)
      i<j : i < j
      i<j = proj₁ (proj₂ (proj₂ f′-good))
      f′i≼f′j : f′ i ≼ f′ j
      f′i≼f′j = proj₂ (proj₂ (proj₂ f′-good))
      i+1+k<j+1+k : i + suc k < j + suc k
      i+1+k<j+1+k = +-monoˡ-< (suc k) i<j
      fi+1+k≼′fj+1+k : f (i + suc k) ≼′ f (j + suc k)
      fi+1+k≼′fj+1+k rewrite f-f′ i | f-f′ j = f′i≼f′j