{-# OPTIONS --without-K --rewriting #-}

open import HoTT
open import homotopy.HSpace renaming (HSpaceStructure to HSS)
open import homotopy.Freudenthal
open import homotopy.IterSuspensionStable
open import homotopy.Pi2HSusp
open import homotopy.EM1HSpace
open import homotopy.EilenbergMacLane1

module homotopy.EilenbergMacLane where

private
  -- helper
  kle : (n : ℕ) → ⟨ S (S n) ⟩ ≤T ⟨ n ⟩ +2+ ⟨ n ⟩
  kle O = inl idp
  kle (S n) = ≤T-trans (≤T-ap-S (kle n))
                (≤T-trans (inl (! (+2+-βr ⟨ n ⟩ ⟨ n ⟩)))
                            (inr ltS))

-- EM(G,n) when G is π₁(A,a₀)
module EMImplicit {i} {X : Ptd i} {{_ : is-connected 0 (de⊙ X)}}
  {{X-level : has-level 1 (de⊙ X)}} (H-X : HSS X) where

  private
    A = de⊙ X
    a₀ = pt X

  ⊙EM : (n : ℕ) → Ptd i
  ⊙EM O = ⊙Ω X
  ⊙EM (S n) = ⊙Trunc ⟨ S n ⟩ (⊙Susp^ n X)

  module _ (n : ℕ) where
    EM = de⊙ (⊙EM n)

  EM-level : (n : ℕ) → has-level ⟨ n ⟩ (EM n)
  EM-level O = has-level-apply X-level _ _
  EM-level (S n) = Trunc-level

  instance
    EM-conn : (n : ℕ) → is-connected ⟨ n ⟩ (EM (S n))
    EM-conn n = Trunc-preserves-conn (⊙Susp^-conn' n)

  {-
  π (S k) (EM (S n)) (embase (S n)) == π k (EM n) (embase n)
  where k > 0 and n = S (S n')
  -}
  module Stable (k n : ℕ) (indexing : S k ≤ S (S n))
    where

    private
      SSn : ℕ
      SSn = S (S n)

      lte : ⟨ S k ⟩ ≤T ⟨ SSn ⟩
      lte = ⟨⟩-monotone-≤ $ indexing

      Skle : S k ≤ (S n) *2
      Skle = ≤-trans indexing (lemma n)
        where lemma : (n' : ℕ) → S (S n') ≤ (S n') *2
              lemma O = inl idp
              lemma (S n') = ≤-trans (≤-ap-S (lemma n')) (inr ltS)

    private
      module SS = Susp^StableSucc k (S n) Skle (⊙Susp^ (S n) X) {{⊙Susp^-conn' (S n)}}

    abstract
      stable : πS (S k) (⊙EM (S SSn)) ≃ᴳ πS k (⊙EM SSn)
      stable =
        πS (S k) (⊙EM (S SSn))
          ≃ᴳ⟨ πS-Trunc-fuse-≤-iso _ _ _ (≤T-ap-S lte) ⟩
        πS (S k) (⊙Susp^ SSn X)
          ≃ᴳ⟨ SS.stable ⟩
        πS k (⊙Susp^ (S n) X)
          ≃ᴳ⟨ πS-Trunc-fuse-≤-iso _ _ _ lte ⁻¹ᴳ ⟩
        πS k (⊙EM SSn)
          ≃ᴳ∎

  module BelowDiagonal where

    π₁ : (n : ℕ) → πS 0 (⊙EM (S (S n))) ≃ᴳ 0ᴳ
    π₁ n =
      contr-iso-0ᴳ (πS 0 (⊙EM (S (S n))))
        (connected-at-level-is-contr
          {{raise-level-≤T (≤T-ap-S (≤T-ap-S (-2≤T ⟨ n ⟩₋₂)))
                          (Trunc-level {n = 0})}})

    -- some clutter here arises from the definition of <;
    -- any simple way to avoid this?
    πS-below : (k n : ℕ) → (S k < n)
      → πS k (⊙EM n) ≃ᴳ 0ᴳ
    πS-below 0 .2 ltS = π₁ 0
    πS-below 0 .3 (ltSR ltS) = π₁ 1
    πS-below 0 (S (S n)) (ltSR (ltSR _)) = π₁ n
    πS-below (S k) ._ ltS =
      πS-below k _ ltS
      ∘eᴳ Stable.stable k k (inr ltS)
    πS-below (S k) ._ (ltSR ltS) =
      πS-below k _ (ltSR ltS)
      ∘eᴳ Stable.stable k (S k) (inr (ltSR ltS))
    πS-below (S k) ._ (ltSR (ltSR ltS)) =
      πS-below k _ (ltSR (ltSR ltS))
      ∘eᴳ Stable.stable k (S (S k)) (inr (ltSR (ltSR ltS)))
    πS-below (S k) (S (S (S n))) (ltSR (ltSR (ltSR lt))) =
      πS-below k _ (<-cancel-S (ltSR (ltSR (ltSR lt))))
      ∘eᴳ Stable.stable k n (inr (<-cancel-S (ltSR (ltSR (ltSR lt)))))

  module OnDiagonal where

    π₁ : πS 0 (⊙EM 1) ≃ᴳ πS 0 X
    π₁ = πS-Trunc-fuse-≤-iso 0 1 X ≤T-refl

    private
      module Π₂ = Pi2HSusp H-X

    π₂ : πS 1 (⊙EM 2) ≃ᴳ πS 0 X
    π₂ = Π₂.π₂-Susp
     ∘eᴳ πS-Trunc-fuse-≤-iso 1 2 (⊙Susp (de⊙ X)) ≤T-refl

    πS-diag : (n : ℕ) → πS n (⊙EM (S n)) ≃ᴳ πS 0 X
    πS-diag 0 = π₁
    πS-diag 1 = π₂
    πS-diag (S (S n)) = πS-diag (S n)
                    ∘eᴳ Stable.stable (S n) n ≤-refl

  module AboveDiagonal where

    πS-above : ∀ (k n : ℕ) → (n < S k)
      → πS k (⊙EM n) ≃ᴳ 0ᴳ
    πS-above k n lt =
      contr-iso-0ᴳ (πS k (⊙EM n))
        (inhab-prop-is-contr
          [ idp^ (S k) ]
          {{Trunc-preserves-level 0 (Ω^-level -1 (S k) _
            (raise-level-≤T (lemma lt) (EM-level n)))}})
      where lemma : {k n : ℕ} → n < k → ⟨ n ⟩ ≤T (⟨ k ⟩₋₂ +2+ -1)
            lemma ltS = inl (! (+2+-comm _ -1))
            lemma (ltSR lt) = ≤T-trans (lemma lt) (inr ltS)

  module Spectrum where

    private
      module Π₂ = Pi2HSusp H-X

    spectrum0 : ⊙Ω (⊙EM 1) ⊙≃ ⊙EM 0
    spectrum0 =
      ⊙Ω (⊙EM 1)
        ⊙≃⟨ ⊙Ω-⊙Trunc-comm 0 X ⟩
      ⊙Trunc 0 (⊙Ω X)
        ⊙≃⟨ ⊙unTrunc-equiv (⊙Ω X) ⟩
      ⊙Ω X ⊙≃∎

    spectrum1 : ⊙Ω (⊙EM 2) ⊙≃ ⊙EM 1
    spectrum1 =
      ⊙Ω (⊙EM 2)
        ⊙≃⟨ ⊙Ω-⊙Trunc-comm 1 (⊙Susp (de⊙ X)) ⟩
      ⊙Trunc 1 (⊙Ω (⊙Susp (de⊙ X)))
        ⊙≃⟨ Π₂.⊙eq ⟩
      ⊙EM 1 ⊙≃∎

    sconn : (n : ℕ) → is-connected ⟨ S n ⟩ (de⊙ (⊙Susp^ (S n) X))
    sconn n = ⊙Susp^-conn' (S n)

    module FS (n : ℕ) =
      FreudenthalEquiv ⟨ n ⟩₋₁ ⟨ S (S n) ⟩ (kle n)
        (⊙Susp^ (S n) X) {{sconn n}}

    Trunc-fmap-σloop-is-equiv : ∀ (n : ℕ)
      → is-equiv (Trunc-fmap {n = ⟨ S n ⟩} (σloop (⊙Susp^ n X)))
    Trunc-fmap-σloop-is-equiv O = snd (Π₂.eq ⁻¹)
    Trunc-fmap-σloop-is-equiv (S n) = snd (FS.eq n)

    spectrumSS : (n : ℕ)
      → ⊙Ω (⊙EM (S (S (S n)))) ⊙≃ ⊙EM (S (S n))
    spectrumSS n =
      ⊙Ω (⊙EM (S (S (S n))))
        ⊙≃⟨ ⊙Ω-⊙Trunc-comm ⟨ S (S n) ⟩ (⊙Susp^ (S (S n)) X) ⟩
      ⊙Trunc ⟨ S (S n) ⟩ (⊙Ω (⊙Susp^ (S (S n)) X))
        ⊙≃⟨ FS.⊙eq n ⊙⁻¹ ⟩
      ⊙EM (S (S n)) ⊙≃∎

    ⊙–>-spectrumSS : ∀ (n : ℕ)
      → ⊙–> (spectrumSS n) ◃⊙idf
        =⊙∘
        FS.⊙encodeN n ◃⊙∘
        ⊙–> (⊙Ω-⊙Trunc-comm ⟨ S (S n) ⟩ (⊙Susp^ (S (S n)) X)) ◃⊙idf
    ⊙–>-spectrumSS n =
      ⊙–> (spectrumSS n) ◃⊙idf
        =⊙∘⟨ =⊙∘-in {gs = ⊙<– (FS.⊙eq n) ◃⊙∘
                          ⊙–> (⊙Ω-⊙Trunc-comm ⟨ S (S n) ⟩ (⊙Susp^ (S (S n)) X)) ◃⊙idf} $
             ⊙λ= (⊙∘-unit-l _) ⟩
      ⊙<– (FS.⊙eq n) ◃⊙∘
      ⊙–> (⊙Ω-⊙Trunc-comm ⟨ S (S n) ⟩ (⊙Susp^ (S (S n)) X)) ◃⊙idf
        =⊙∘₁⟨ 0 & 1 & FS.⊙<–-⊙eq n ⟩
      FS.⊙encodeN n ◃⊙∘
      ⊙–> (⊙Ω-⊙Trunc-comm ⟨ S (S n) ⟩ (⊙Susp^ (S (S n)) X)) ◃⊙idf ∎⊙∘

    spectrum : (n : ℕ) → ⊙Ω (⊙EM (S n)) ⊙≃ ⊙EM n
    spectrum 0 = spectrum0
    spectrum 1 = spectrum1
    spectrum (S (S n)) = spectrumSS n

module EMImplicitMap {i} {j} {X : Ptd i} {Y : Ptd j} (f : X ⊙→ Y)
  {{_ : is-connected 0 (de⊙ X)}} {{_ : is-connected 0 (de⊙ Y)}}
  {{X-level : has-level 1 (de⊙ X)}} {{Y-level : has-level 1 (de⊙ Y)}}
  (H-X : HSS X) (H-Y : HSS Y) where

  ⊙EM-fmap : ∀ n → EMImplicit.⊙EM H-X n ⊙→ EMImplicit.⊙EM H-Y n
  ⊙EM-fmap O = ⊙Ω-fmap f
  ⊙EM-fmap (S n) = ⊙Trunc-fmap (⊙Susp^-fmap n f)

module SpectrumNatural {i} {X Y : Ptd i} (f : X ⊙→ Y)
  {{_ : is-connected 0 (de⊙ X)}} {{_ : is-connected 0 (de⊙ Y)}}
  {{X-level : has-level 1 (de⊙ X)}} {{Y-level : has-level 1 (de⊙ Y)}}
  (H-X : HSS X) (H-Y : HSS Y) where

  open EMImplicitMap f H-X H-Y

  open EMImplicit.Spectrum

  ⊙–>-spectrum0-natural :
    ⊙–> (spectrum0 H-Y) ◃⊙∘
    ⊙Ω-fmap (⊙Trunc-fmap f) ◃⊙idf
    =⊙∘
    ⊙Ω-fmap f ◃⊙∘
    ⊙–> (spectrum0 H-X) ◃⊙idf
  ⊙–>-spectrum0-natural =
    ⊙–> (spectrum0 H-Y) ◃⊙∘
    ⊙Ω-fmap (⊙Trunc-fmap f) ◃⊙idf
      =⊙∘⟨ 0 & 1 & ⊙expand (⊙–> (⊙unTrunc-equiv (⊙Ω Y)) ◃⊙∘
                            ⊙–> (⊙Ω-⊙Trunc-comm 0 Y) ◃⊙idf) ⟩
    ⊙–> (⊙unTrunc-equiv (⊙Ω Y)) ◃⊙∘
    ⊙–> (⊙Ω-⊙Trunc-comm 0 Y) ◃⊙∘
    ⊙Ω-fmap (⊙Trunc-fmap f) ◃⊙idf
      =⊙∘⟨ 1 & 2 & ⊙–>-⊙Ω-⊙Trunc-comm-natural-=⊙∘ 0 f ⟩
    ⊙–> (⊙unTrunc-equiv (⊙Ω Y)) ◃⊙∘
    ⊙Trunc-fmap (⊙Ω-fmap f) ◃⊙∘
    ⊙–> (⊙Ω-⊙Trunc-comm (S (S ⟨-2⟩)) X) ◃⊙idf
      =⊙∘⟨ 0 & 2 & ⊙–>-⊙unTrunc-equiv-natural-=⊙∘ (⊙Ω-fmap f) ⟩
    ⊙Ω-fmap f ◃⊙∘
    ⊙–> (⊙unTrunc-equiv (⊙Ω X)) ◃⊙∘
    ⊙–> (⊙Ω-⊙Trunc-comm (S (S ⟨-2⟩)) X) ◃⊙idf
      =⊙∘⟨ 1 & 2 & ⊙contract ⟩
    ⊙Ω-fmap f ◃⊙∘
    ⊙–> (spectrum0 H-X) ◃⊙idf ∎⊙∘

  ⊙–>-spectrum1-natural :
    ⊙–> (spectrum1 H-Y) ◃⊙∘
    ⊙Ω-fmap (⊙Trunc-fmap (⊙Susp-fmap (fst f))) ◃⊙idf
    =⊙∘
    ⊙Trunc-fmap f ◃⊙∘
    ⊙–> (spectrum1 H-X) ◃⊙idf
  ⊙–>-spectrum1-natural =
    ⊙–> (spectrum1 H-Y) ◃⊙∘
    ⊙Ω-fmap (⊙Trunc-fmap (⊙Susp-fmap (fst f))) ◃⊙idf
      =⊙∘⟨ 0 & 1 & ⊙expand (⊙–> (Pi2HSusp.⊙eq H-Y) ◃⊙∘
                            ⊙–> (⊙Ω-⊙Trunc-comm 1 (⊙Susp (de⊙ Y))) ◃⊙idf) ⟩
    ⊙–> (Pi2HSusp.⊙eq H-Y) ◃⊙∘
    ⊙–> (⊙Ω-⊙Trunc-comm 1 (⊙Susp (de⊙ Y))) ◃⊙∘
    ⊙Ω-fmap (⊙Trunc-fmap (⊙Susp-fmap (fst f))) ◃⊙idf
      =⊙∘⟨ 1 & 2 & ⊙–>-⊙Ω-⊙Trunc-comm-natural-=⊙∘ 1 (⊙Susp-fmap (fst f)) ⟩
    ⊙–> (Pi2HSusp.⊙eq H-Y) ◃⊙∘
    ⊙Trunc-fmap (⊙Ω-fmap (⊙Susp-fmap (fst f))) ◃⊙∘
    ⊙–> (⊙Ω-⊙Trunc-comm 1 (⊙Susp (de⊙ X))) ◃⊙idf
      =⊙∘⟨ 0 & 2 & Pi2HSuspNaturality.⊙encodeN-natural f H-X H-Y ⟩
    ⊙Trunc-fmap f ◃⊙∘
    ⊙–> (Pi2HSusp.⊙eq H-X) ◃⊙∘
    ⊙–> (⊙Ω-⊙Trunc-comm (S (S (S ⟨-2⟩))) (⊙Susp (de⊙ X))) ◃⊙idf
      =⊙∘⟨ 1 & 2 & ⊙contract ⟩
    ⊙Trunc-fmap f ◃⊙∘
    ⊙–> (spectrum1 H-X) ◃⊙idf ∎⊙∘

  ⊙–>-spectrumSS-natural : ∀ (n : ℕ)
    → ⊙–> (spectrumSS H-Y n) ◃⊙∘
      ⊙Ω-fmap (⊙Trunc-fmap (⊙Susp^-fmap (S (S n)) f)) ◃⊙idf
      =⊙∘
      ⊙Trunc-fmap (⊙Susp^-fmap (S n) f) ◃⊙∘
      ⊙–> (spectrumSS H-X n) ◃⊙idf
  ⊙–>-spectrumSS-natural n =
    ⊙–> (spectrumSS H-Y n) ◃⊙∘
    ⊙Ω-fmap (⊙Trunc-fmap (⊙Susp^-fmap (S (S n)) f)) ◃⊙idf
      =⊙∘⟨ 0 & 1 & ⊙–>-spectrumSS H-Y n ⟩
    FS.⊙encodeN H-Y n ◃⊙∘
    ⊙–> (⊙Ω-⊙Trunc-comm ⟨ S (S n) ⟩ (⊙Susp^ (S (S n)) Y)) ◃⊙∘
    ⊙Ω-fmap (⊙Trunc-fmap (⊙Susp^-fmap (S (S n)) f)) ◃⊙idf
      =⊙∘⟨ 1 & 2 & ⊙–>-⊙Ω-⊙Trunc-comm-natural-=⊙∘ ⟨ S (S n) ⟩ (⊙Susp^-fmap (S (S n)) f) ⟩
    FS.⊙encodeN H-Y n ◃⊙∘
    ⊙Trunc-fmap (⊙Ω-fmap (⊙Susp^-fmap (S (S n)) f)) ◃⊙∘
    ⊙–> (⊙Ω-⊙Trunc-comm ⟨ S (S n) ⟩ (⊙Susp^ (S (S n)) X)) ◃⊙idf
      =⊙∘⟨ 0 & 2 & FreudenthalEquivNatural.⊙encodeN-natural
             ⟨ n ⟩₋₁ ⟨ S (S n) ⟩ (kle n)
             {X = ⊙Susp^ (S n) X} {Y = ⊙Susp^ (S n) Y} (⊙Susp^-fmap (S n) f)
             {{sconn H-X n}} {{sconn H-Y n}} ⟩
    ⊙Trunc-fmap (⊙Susp^-fmap (S n) f) ◃⊙∘
    FS.⊙encodeN H-X n ◃⊙∘
    ⊙–> (⊙Ω-⊙Trunc-comm ⟨ S (S n) ⟩ (⊙Susp^ (S (S n)) X)) ◃⊙idf
      =⊙∘⟨ 1 & 2 & !⊙∘ $ ⊙–>-spectrumSS H-X n ⟩
    ⊙Trunc-fmap (⊙Susp^-fmap (S n) f) ◃⊙∘
    ⊙–> (spectrumSS H-X n) ◃⊙idf ∎⊙∘

  abstract
    ⊙–>-spectrum-natural : ∀ (n : ℕ)
      → ⊙–> (spectrum H-Y n) ◃⊙∘
        ⊙Ω-fmap (⊙EM-fmap (S n)) ◃⊙idf
        =⊙∘
        ⊙EM-fmap n ◃⊙∘
        ⊙–> (spectrum H-X n) ◃⊙idf
    ⊙–>-spectrum-natural 0 = ⊙–>-spectrum0-natural
    ⊙–>-spectrum-natural 1 = ⊙–>-spectrum1-natural
    ⊙–>-spectrum-natural (S (S n)) = ⊙–>-spectrumSS-natural n

    ⊙<–-spectrum-natural : ∀ (n : ℕ)
      → ⊙<– (spectrum H-Y n) ◃⊙∘
        ⊙EM-fmap n ◃⊙idf
        =⊙∘
        ⊙Ω-fmap (⊙EM-fmap (S n)) ◃⊙∘
        ⊙<– (spectrum H-X n) ◃⊙idf
    ⊙<–-spectrum-natural n =
      ⊙<– (spectrum H-Y n) ◃⊙∘
      ⊙EM-fmap n ◃⊙idf
        =⊙∘⟨ 2 & 0 & !⊙∘ $ ⊙<–-inv-r-=⊙∘ (spectrum H-X n) ⟩
      ⊙<– (spectrum H-Y n) ◃⊙∘
      ⊙EM-fmap n ◃⊙∘
      ⊙–> (spectrum H-X n) ◃⊙∘
      ⊙<– (spectrum H-X n) ◃⊙idf
        =⊙∘⟨ 1 & 2 & !⊙∘ (⊙–>-spectrum-natural n) ⟩
      ⊙<– (spectrum H-Y n) ◃⊙∘
      ⊙–> (spectrum H-Y n) ◃⊙∘
      ⊙Ω-fmap (⊙Trunc-fmap (⊙Susp^-fmap n f)) ◃⊙∘
      ⊙<– (spectrum H-X n) ◃⊙idf
        =⊙∘⟨ 0 & 2 & ⊙<–-inv-l-=⊙∘ (spectrum H-Y n) ⟩
      ⊙Ω-fmap (⊙Trunc-fmap (⊙Susp^-fmap n f)) ◃⊙∘
      ⊙<– (spectrum H-X n) ◃⊙idf ∎⊙∘

module EMExplicit {i} (G : AbGroup i) where
  module HSpace = EM₁HSpace G
  open EMImplicit
    {X = ⊙EM₁ (AbGroup.grp G)}
    {{EM₁-conn}}
    {{EM₁-level₁ (AbGroup.grp G)}}
    HSpace.H-⊙EM₁ public

  open BelowDiagonal public using (πS-below)

  πS-diag : (n : ℕ) → πS n (⊙EM (S n)) ≃ᴳ AbGroup.grp G
  πS-diag n = π₁-EM₁ (AbGroup.grp G) ∘eᴳ OnDiagonal.πS-diag n

  open AboveDiagonal public using (πS-above)

  abstract
    spectrum : (n : ℕ) → ⊙Ω (⊙EM (S n)) ⊙≃ ⊙EM n
    spectrum = Spectrum.spectrum

    spectrum-def : ∀ n → spectrum n == Spectrum.spectrum n
    spectrum-def n = idp


module Data.Num.Bijective.Properties where

open import Data.Num.Bijective

open import Data.Nat
open import Data.Nat.DM
open import Data.Nat.Properties
open import Data.Nat.Properties.Simple
open import Data.Nat.Properties.Extra
open import Data.Fin                    as Fin using (Fin)
open import Data.Fin.Extra
open import Data.Fin.Properties         as FinP hiding (_≟_)
    renaming (toℕ-injective to Fin-toℕ-injective)
open import Data.Fin.Properties.Extra   as FinPX
    renaming (cancel-suc to Fin-cancel-suc)

open import Data.Product
open import Function
open import Function.Injection hiding (_∘_)
open import Relation.Nullary
open import Relation.Nullary.Negation using (contradiction)
open import Relation.Binary.PropositionalEquality as PropEq
open ≡-Reasoning

-- begin
--     {!   !}
-- ≡⟨ {!   !} ⟩
--     {!   !}
-- ≡⟨ {!   !} ⟩
--     {!   !}
-- ≡⟨ {!   !} ⟩
--     {!   !}
-- ≡⟨ {!   !} ⟩
--     {!   !}
-- ∎

------------------------------------------------------------------------
-- Various ≡-related lemmata
------------------------------------------------------------------------

1+-never-∙ : ∀ {b} xs → 1+ {b} xs ≢ ∙
1+-never-∙     ∙        ()
1+-never-∙ {b} (x ∷ xs) +1xs≡∙ with full x
1+-never-∙     (x ∷ xs) () | yes p
1+-never-∙     (x ∷ xs) () | no ¬p

fromℕ-∙-0 : ∀ {b} n → fromℕ {b} n ≡ ∙ → n ≡ 0
fromℕ-∙-0 zero    p = refl
fromℕ-∙-0 (suc n) p = contradiction p (1+-never-∙ (fromℕ n))

toℕ-∙-0 : ∀ {b} xs → toℕ {suc b} xs ≡ 0 → xs ≡ ∙
toℕ-∙-0 ∙ p = refl
toℕ-∙-0 (x ∷ xs) ()

digit+1-never-0 : ∀ {b} (x : Fin (suc b)) → (¬p : Fin.toℕ x ≢ b) → digit+1 x ¬p ≢ Fin.zero
digit+1-never-0 {b}     x            ¬p eq with Fin.toℕ x ≟ b
digit+1-never-0 {b}     x            ¬p eq | yes q = contradiction q ¬p
digit+1-never-0 {zero}  Fin.zero     ¬p eq | no ¬q = contradiction refl ¬p
digit+1-never-0 {suc b} Fin.zero     ¬p eq | no ¬q = contradiction eq (λ ())
digit+1-never-0 {zero}  (Fin.suc ()) ¬p eq | no ¬q
digit+1-never-0 {suc b} (Fin.suc x)  ¬p () | no ¬q

Fin⇒ℕ-digit+1 : ∀ {b}
    → (x : Fin (suc b))
    → (¬p : Fin.toℕ x ≢ b)
    → Fin.toℕ (digit+1 x ¬p) ≡ suc (Fin.toℕ x)
Fin⇒ℕ-digit+1 {b} x ¬p = toℕ-fromℕ≤ (s≤s (digit+1-lemma (Fin.toℕ x) b (bounded x) ¬p))

------------------------------------------------------------------------
-- injectivity
------------------------------------------------------------------------

∷-injective : ∀ {b}
    → {x y : Fin (suc b)}
    → {xs ys : Bij (suc b)}
    → x ∷ xs ≡ y ∷ ys
    → x ≡ y × xs ≡ ys
∷-injective refl = refl , refl

digit+1-injective :  ∀ {b} (x y : Fin (suc b))
    → (¬p : Fin.toℕ x ≢ b)
    → (¬q : Fin.toℕ y ≢ b)
    → digit+1 x ¬p ≡ digit+1 y ¬q
    → x ≡ y
digit+1-injective         Fin.zero     Fin.zero     ¬p ¬q eq = refl
digit+1-injective {zero}  Fin.zero     (Fin.suc ()) ¬p ¬q eq
digit+1-injective {suc b} Fin.zero     (Fin.suc y)  ¬p ¬q eq = Fin-toℕ-injective (cancel-suc eq')
    where   eq' : Fin.toℕ {suc (suc b)} (digit+1 Fin.zero ¬p) ≡ suc (Fin.toℕ {suc (suc b)} (Fin.suc y))
            eq' = begin
                    Fin.toℕ {suc (suc b)} (Fin.suc Fin.zero)
                ≡⟨ cong Fin.toℕ eq ⟩
                    suc (Fin.toℕ (Fin.fromℕ≤ (s≤s (digit+1-lemma (Fin.toℕ y) b (bounded y) (¬q ∘ cong suc)))))
                ≡⟨ cong suc (toℕ-fromℕ≤ (s≤s (digit+1-lemma (Fin.toℕ y) b (bounded y) (¬q ∘ cong suc)))) ⟩
                    suc (Fin.toℕ {suc (suc b)} (Fin.suc y))
                ∎
digit+1-injective {zero}  (Fin.suc ()) Fin.zero    ¬p ¬q eq
digit+1-injective {suc b} (Fin.suc x)  Fin.zero    ¬p ¬q eq = Fin-toℕ-injective (cancel-suc eq')
    where   eq' : suc (Fin.toℕ {suc (suc b)} (Fin.suc x)) ≡ Fin.toℕ {suc (suc b)} (digit+1 Fin.zero ¬q)
            eq' = begin
                    suc (Fin.toℕ {suc (suc b)} (Fin.suc x))
                ≡⟨ cong suc (sym (toℕ-fromℕ≤ (s≤s (digit+1-lemma (Fin.toℕ x) b (bounded x) (¬p ∘ cong suc))))) ⟩
                    suc (Fin.toℕ (Fin.fromℕ≤ (s≤s (digit+1-lemma (Fin.toℕ x) b (bounded x) (¬p ∘ cong suc)))))
                ≡⟨ cong Fin.toℕ eq ⟩
                    Fin.toℕ {suc (suc b)} (Fin.suc Fin.zero)
                ∎
digit+1-injective {zero}  (Fin.suc ()) (Fin.suc y) ¬p ¬q eq
digit+1-injective {suc b} (Fin.suc x)  (Fin.suc y) ¬p ¬q eq = cong Fin.suc (digit+1-injective x y (¬p ∘ cong suc) (¬q ∘ cong suc) (Fin-cancel-suc eq))
    where   open import Data.Fin.Properties.Extra

1+-injective : ∀ b → (xs ys : Bij (suc b)) → 1+ xs ≡ 1+ ys → xs ≡ ys
1+-injective b ∙        ∙        eq = refl
1+-injective b ∙        (y ∷ ys) eq with full y
1+-injective b ∙        (y ∷ ys) eq | yes p = contradiction (proj₂ (∷-injective (sym eq))) (1+-never-∙ ys)
1+-injective b ∙        (y ∷ ys) eq | no ¬p = contradiction (proj₁ (∷-injective (sym eq))) (digit+1-never-0 y ¬p)
1+-injective b (x ∷ xs) ∙        eq with full x
1+-injective b (x ∷ xs) ∙        eq | yes p = contradiction (proj₂ (∷-injective eq)) (1+-never-∙ xs)
1+-injective b (x ∷ xs) ∙        eq | no ¬p = contradiction (proj₁ (∷-injective eq)) (digit+1-never-0 x ¬p)
1+-injective b (x ∷ xs) (y ∷ ys) eq with full x | full y
1+-injective b (x ∷ xs) (y ∷ ys) eq | yes p | yes q = cong₂ _∷_ x≡y xs≡ys
    where
            x≡y : x ≡ y
            x≡y = Fin-toℕ-injective (trans p (sym q))
            xs≡ys : xs ≡ ys
            xs≡ys = 1+-injective b xs ys (proj₂ (∷-injective eq))
1+-injective b (x ∷ xs) (y ∷ ys) eq | yes p | no ¬q = contradiction (sym (proj₁ (∷-injective eq))) (digit+1-never-0 y ¬q)
1+-injective b (x ∷ xs) (y ∷ ys) eq | no ¬p | yes q = contradiction (proj₁ (∷-injective eq))       (digit+1-never-0 x ¬p)
1+-injective b (x ∷ xs) (y ∷ ys) eq | no ¬p | no ¬q = cong₂ _∷_ (digit+1-injective x y ¬p ¬q (proj₁ (∷-injective eq))) (proj₂ (∷-injective eq))

toℕ-injective : ∀ {b} → (xs ys : Bij (suc b)) → toℕ xs ≡ toℕ ys → xs ≡ ys
toℕ-injective ∙        ∙        eq = refl
toℕ-injective ∙        (y ∷ ys) ()
toℕ-injective (x ∷ xs) ∙        ()
toℕ-injective (x ∷ xs) (y ∷ ys) eq =
    cong₂ _∷_ (proj₁ ind) (toℕ-injective xs ys (proj₂ ?))
    where
            ind : x ≡ y × toℕ xs ≡ toℕ ys
            ind = some-lemma x y (toℕ xs) (toℕ ys) (cancel-suc eq)

fromℕ-injective : ∀ b m n → fromℕ {b} m ≡ fromℕ {b} n → m ≡ n
fromℕ-injective b zero    zero    eq = refl
fromℕ-injective b zero    (suc n) eq = contradiction (sym eq) (1+-never-∙ (fromℕ n))
fromℕ-injective b (suc m) zero    eq = contradiction eq       (1+-never-∙ (fromℕ m))
fromℕ-injective b (suc m) (suc n) eq = cong suc (fromℕ-injective b m n (1+-injective b (fromℕ m) (fromℕ n) eq))

--
--      xs ─── 1+ ──➞ 1+ xs
--      |                |
--     toℕ              toℕ
--      ↓                ↓
--      n ─── suc ──➞ suc n
--
toℕ-1+ : ∀ b xs → toℕ {suc b} (1+ xs) ≡ suc (toℕ xs)
toℕ-1+ b ∙        = refl
toℕ-1+ b (x ∷ xs) with full x
toℕ-1+ b (x ∷ xs) | yes p =
    begin
        toℕ (Fin.zero ∷ 1+ xs)
    ≡⟨ cong (λ w → suc (w * suc b)) (toℕ-1+ b xs) ⟩
        suc (suc (b + toℕ xs * suc b))
    ≡⟨ cong (λ w → suc (suc (w + toℕ xs * suc b))) (sym p) ⟩
        suc (toℕ (x ∷ xs))
    ∎
toℕ-1+ b (x ∷ xs) | no ¬p =
    cong (λ w → suc w + toℕ xs * suc b) (toℕ-fromℕ≤ (s≤s (digit+1-lemma (Fin.toℕ x) b (bounded x) ¬p)))

--
--      xs ── n+ n ──➞ n+ n xs
--      |                  |
--     toℕ               toℕ
--      ↓                  ↓
--      m ──   n + ──➞ n + m
--

toℕ-n+ : ∀ b n xs → toℕ {suc b} (n+ n xs) ≡ n + (toℕ xs)
toℕ-n+ b zero xs = refl
toℕ-n+ b (suc n) xs = begin
        toℕ (1+ (n+ n xs))
    ≡⟨ toℕ-1+ b (n+ n xs) ⟩
        suc (toℕ (n+ n xs))
    ≡⟨ cong suc (toℕ-n+ b n xs) ⟩
        suc (n + toℕ xs)
    ∎

toℕ-fromℕ : ∀ b n → toℕ {suc b} (fromℕ {b} n) ≡ n
toℕ-fromℕ b zero    = refl
toℕ-fromℕ b (suc n) with fromℕ {b} n | inspect (fromℕ {b}) n
toℕ-fromℕ b (suc n) | ∙      | [ eq ] = cong suc (sym (fromℕ-∙-0 n eq))
toℕ-fromℕ b (suc n) | x ∷ xs | [ eq ] with full x
toℕ-fromℕ b (suc n) | x ∷ xs | [ eq ] | yes p =
    begin
        toℕ (Fin.zero ∷ 1+ xs)
    ≡⟨ refl ⟩
        suc (toℕ (1+ xs) * suc b)
    ≡⟨ cong (λ w → suc (w * suc b)) (toℕ-1+ b xs) ⟩
        suc (suc (b + toℕ xs * suc b))
    ≡⟨ cong (λ w → suc (suc (w + toℕ xs * suc b))) (sym p) ⟩
        suc (suc (Fin.toℕ x) + toℕ xs * suc b)
    ≡⟨ cong (λ w → suc (toℕ w)) (sym eq) ⟩
        suc (toℕ (fromℕ n))
    ≡⟨ cong suc (toℕ-fromℕ b n) ⟩
        suc n
    ∎
toℕ-fromℕ b (suc n) | x ∷ xs | [ eq ] | no ¬p =
    begin
        toℕ (digit+1 x ¬p ∷ xs)
    ≡⟨ cong (λ w → suc (w + toℕ xs * suc b)) (toℕ-fromℕ≤ (s≤s (digit+1-lemma (Fin.toℕ x) b (bounded x) ¬p))) ⟩
        suc (suc (Fin.toℕ x + toℕ xs * suc b))
    ≡⟨ cong (λ w → suc (toℕ w)) (sym eq) ⟩
        suc (toℕ (fromℕ n))
    ≡⟨ cong suc (toℕ-fromℕ b n) ⟩
        suc n
    ∎

fromℕ-toℕ : ∀ {b} xs → fromℕ {b} (toℕ {suc b} xs) ≡ xs
fromℕ-toℕ {b} xs = toℕ-injective (fromℕ (toℕ xs)) xs (toℕ-fromℕ b (toℕ xs))

toℕ-fromℕ-reverse : ∀ b {m} {n} → toℕ (fromℕ {b} m) ≡ n → m ≡ n
toℕ-fromℕ-reverse b {m} {n} eq = begin
        m
    ≡⟨ sym (toℕ-fromℕ b m) ⟩
        toℕ (fromℕ m)
    ≡⟨ eq ⟩
        n
    ∎

fromℕ-toℕ-reverse : ∀ b {xs} {ys} → fromℕ {b} (toℕ {suc b} xs) ≡ ys → xs ≡ ys
fromℕ-toℕ-reverse b {xs} {ys} eq = begin
        xs
    ≡⟨ sym (fromℕ-toℕ xs) ⟩
        fromℕ (toℕ xs)
    ≡⟨ eq ⟩
        ys
    ∎

------------------------------------------------------------------------
-- surjectivity
------------------------------------------------------------------------

toℕ-surjective : ∀ b n → Σ[ xs ∈ Bij (suc b) ] toℕ xs ≡ n
toℕ-surjective b zero    = ∙               , refl
toℕ-surjective b (suc n) = (fromℕ (suc n)) , toℕ-fromℕ b (suc n)

fromℕ-surjective : ∀ {b} (xs : Bij (suc b)) → Σ[ n ∈ ℕ ] fromℕ n ≡ xs
fromℕ-surjective ∙ = zero , refl
fromℕ-surjective (x ∷ xs) = toℕ (x ∷ xs) , fromℕ-toℕ (x ∷ xs)

------------------------------------------------------------------------
-- _⊹_
------------------------------------------------------------------------

toℕ-⊹-homo : ∀ {b} (xs ys : Bij (suc b)) → toℕ (xs ⊹ ys) ≡ toℕ xs + toℕ ys
toℕ-⊹-homo {b} ∙        ys       = refl
toℕ-⊹-homo {b} (x ∷ xs) ∙        = sym (+-right-identity (suc (Fin.toℕ x + toℕ xs * suc b)))
toℕ-⊹-homo {b} (x ∷ xs) (y ∷ ys) with suc (Fin.toℕ x + Fin.toℕ y) divMod (suc b)
toℕ-⊹-homo {b} (x ∷ xs) (y ∷ ys) | result quotient remainder property div-eq mod-eq
    rewrite div-eq | mod-eq
    = begin
            suc (Fin.toℕ remainder + toℕ (n+ quotient (xs ⊹ ys)) * suc b)
        -- apply "toℕ-n+" to remove "n+"
        ≡⟨ cong (λ w → suc (Fin.toℕ remainder + w * suc b)) (toℕ-n+ b quotient (xs ⊹ ys)) ⟩
            suc (Fin.toℕ remainder + (quotient + toℕ (xs ⊹ ys)) * suc b)
        -- the induction hypothesis
        ≡⟨ cong (λ w → suc (Fin.toℕ remainder + (quotient + w) * suc b)) (toℕ-⊹-homo xs ys) ⟩
            suc (Fin.toℕ remainder + (quotient + (toℕ xs + toℕ ys)) * suc b)
        ≡⟨ cong (λ w → suc (Fin.toℕ remainder) + w) (distribʳ-*-+ (suc b) quotient (toℕ xs + toℕ ys)) ⟩
            suc (Fin.toℕ remainder + (quotient * suc b + (toℕ xs + toℕ ys) * suc b))
        ≡⟨ cong suc (sym (+-assoc (Fin.toℕ remainder) (quotient * suc b) ((toℕ xs + toℕ ys) * suc b))) ⟩
            suc (Fin.toℕ remainder + quotient * suc b + (toℕ xs + toℕ ys) * suc b)
        -- apply "property"
        ≡⟨ cong (λ w → suc (w + (toℕ xs + toℕ ys) * suc b)) (sym property) ⟩
            suc (suc (Fin.toℕ x + Fin.toℕ y + (toℕ xs + toℕ ys) * suc b))
        ≡⟨ cong (λ w → suc (suc (Fin.toℕ x + Fin.toℕ y + w))) (distribʳ-*-+ (suc b) (toℕ xs) (toℕ ys)) ⟩
            suc (suc (Fin.toℕ x + Fin.toℕ y + (toℕ xs * suc b + toℕ ys * suc b)))
        ≡⟨ cong (λ w → suc (suc w)) (+-assoc (Fin.toℕ x) (Fin.toℕ y) (toℕ xs * suc b + toℕ ys * suc b)) ⟩
            suc (suc (Fin.toℕ x + (Fin.toℕ y + (toℕ xs * suc b + toℕ ys * suc b))))
        ≡⟨ cong (λ w → suc (suc (Fin.toℕ x + w))) (sym (+-assoc (Fin.toℕ y) (toℕ xs * suc b) (toℕ ys * suc b))) ⟩
            suc (suc (Fin.toℕ x + (Fin.toℕ y + toℕ xs * suc b + toℕ ys * suc b)))
        ≡⟨ cong (λ w → suc (suc (Fin.toℕ x + (w + toℕ ys * suc b)))) (+-comm (Fin.toℕ y) (toℕ xs * suc b)) ⟩
            suc (suc (Fin.toℕ x + (toℕ xs * suc b + Fin.toℕ y + toℕ ys * suc b)))
        ≡⟨ cong (λ w → suc (suc (Fin.toℕ x + w))) (+-assoc (toℕ xs * suc b) (Fin.toℕ y) (toℕ ys * suc b)) ⟩
            suc (suc (Fin.toℕ x + (toℕ xs * suc b + (Fin.toℕ y + toℕ ys * suc b))))
        ≡⟨ cong (λ w → suc (suc w)) (sym (+-assoc (Fin.toℕ x) (toℕ xs * suc b) (Fin.toℕ y + toℕ ys * suc b))) ⟩
            suc (suc ((Fin.toℕ x + toℕ xs * suc b) + (Fin.toℕ y + toℕ ys * suc b)))
        ≡⟨ cong suc (sym (+-suc (Fin.toℕ x + toℕ xs * suc b) (Fin.toℕ y + toℕ ys * suc b))) ⟩
            suc (Fin.toℕ x + toℕ xs * suc b + suc (Fin.toℕ y + toℕ ys * suc b))
        ≡⟨ refl ⟩
            toℕ (x ∷ xs) + toℕ (y ∷ ys)
        ∎

fromℕ-⊹-homo : ∀ {b} (m n : ℕ) → fromℕ {b} (m + n) ≡ fromℕ m ⊹ fromℕ n
fromℕ-⊹-homo {b} m n = toℕ-injective (fromℕ (m + n)) (fromℕ m ⊹ fromℕ n) $
    begin
        toℕ (fromℕ (m + n))
    ≡⟨ toℕ-fromℕ b (m + n) ⟩
        m + n
    ≡⟨ sym (cong₂ _+_ (toℕ-fromℕ b m) (toℕ-fromℕ b n)) ⟩
        toℕ (fromℕ m) + toℕ (fromℕ n)
    ≡⟨ sym (toℕ-⊹-homo (fromℕ m) (fromℕ n)) ⟩
        toℕ (fromℕ m ⊹ fromℕ n)
    ∎

⊹-1+ : ∀ {b} (xs ys : Bij (suc b)) → (1+ xs) ⊹ ys ≡ 1+ (xs ⊹ ys)
⊹-1+ {b} xs ys = toℕ-injective (1+ xs ⊹ ys) (1+ (xs ⊹ ys)) $
    begin
        toℕ (1+ xs ⊹ ys)
    ≡⟨ toℕ-⊹-homo (1+ xs) ys ⟩
        toℕ (1+ xs) + toℕ ys
    ≡⟨ cong (λ w → w + toℕ ys) (toℕ-1+ b xs) ⟩
        suc (toℕ xs + toℕ ys)
    ≡⟨ cong suc (sym (toℕ-⊹-homo xs ys)) ⟩
        suc (toℕ (xs ⊹ ys))
    ≡⟨ sym (toℕ-1+ b (xs ⊹ ys)) ⟩
        toℕ (1+ (xs ⊹ ys))
    ∎

⊹-assoc : ∀ {b} (xs ys zs : Bij (suc b)) → (xs ⊹ ys) ⊹ zs ≡ xs ⊹ (ys ⊹ zs)
⊹-assoc xs ys zs = toℕ-injective (xs ⊹ ys ⊹ zs) (xs ⊹ (ys ⊹ zs)) $
    begin
        toℕ (xs ⊹ ys ⊹ zs)
    ≡⟨ toℕ-⊹-homo (xs ⊹ ys) zs ⟩
        toℕ (xs ⊹ ys) + toℕ zs
    ≡⟨ cong (λ w → w + toℕ zs) (toℕ-⊹-homo xs ys) ⟩
        toℕ xs + toℕ ys + toℕ zs
    ≡⟨ +-assoc (toℕ xs) (toℕ ys) (toℕ zs) ⟩
        toℕ xs + (toℕ ys + toℕ zs)
    ≡⟨ cong (λ w → toℕ xs + w) (sym (toℕ-⊹-homo ys zs)) ⟩
        toℕ xs + toℕ (ys ⊹ zs)
    ≡⟨ sym (toℕ-⊹-homo xs (ys ⊹ zs)) ⟩
        toℕ (xs ⊹ (ys ⊹ zs))
    ∎

⊹-comm : ∀ {b} (xs ys : Bij (suc b)) → xs ⊹ ys ≡ ys ⊹ xs
⊹-comm xs ys = toℕ-injective (xs ⊹ ys) (ys ⊹ xs) $
    begin
        toℕ (xs ⊹ ys)
    ≡⟨ toℕ-⊹-homo xs ys ⟩
        toℕ xs + toℕ ys
    ≡⟨ +-comm (toℕ xs) (toℕ ys) ⟩
        toℕ ys + toℕ xs
    ≡⟨ sym (toℕ-⊹-homo ys xs) ⟩
        toℕ (ys ⊹ xs)
    ∎

increase-base-lemma : ∀ b
    → (xs : Bij (suc b))
    → toℕ (increase-base xs) ≡ toℕ xs
increase-base-lemma b ∙ = refl
increase-base-lemma b (x ∷ xs) with fromℕ {suc b} (toℕ (increase-base xs) * suc b) | inspect (λ ws → fromℕ {suc b} (toℕ ws * suc b)) (increase-base xs)
increase-base-lemma b (x ∷ xs) | ∙      | [ eq ] = cong suc $
    begin
        Fin.toℕ (Fin.inject₁ x) + 0
    ≡⟨ cong (λ w → w + 0) (FinP.inject₁-lemma x) ⟩
        Fin.toℕ x + 0
    ≡⟨ cong (λ w → Fin.toℕ x + w) $
            begin
                zero
            ≡⟨ cong toℕ (sym eq) ⟩
                toℕ (fromℕ (toℕ (increase-base xs) * suc b))
            ≡⟨ toℕ-fromℕ (suc b) (toℕ (increase-base xs) * suc b) ⟩
                toℕ (increase-base xs) * suc b
            ∎ ⟩
        Fin.toℕ x + toℕ (increase-base xs) * suc b
    ≡⟨ cong (λ w → Fin.toℕ x + w * suc b) (increase-base-lemma b xs) ⟩
        Fin.toℕ x + toℕ xs * suc b
    ∎
increase-base-lemma b (x ∷ xs) | y ∷ ys | [ eq ] with (suc (Fin.toℕ x + Fin.toℕ y)) divMod (suc (suc b))
increase-base-lemma b (x ∷ xs) | y ∷ ys | [ eq ] | result quotient remainder property div-eq mod-eq
    rewrite div-eq | mod-eq
    = cong suc $ begin
            Fin.toℕ remainder + toℕ (n+ quotient ys) * suc (suc b)
        ≡⟨ cong (λ w → Fin.toℕ remainder + w * suc (suc b)) (toℕ-n+ (suc b) quotient ys) ⟩
            Fin.toℕ remainder + (quotient + toℕ ys) * suc (suc b)
        ≡⟨ cong (λ w → Fin.toℕ remainder + w) (distribʳ-*-+ (suc (suc b)) quotient (toℕ ys)) ⟩
            Fin.toℕ remainder + (quotient * suc (suc b) + toℕ ys * suc (suc b))
        ≡⟨ sym (+-assoc (Fin.toℕ remainder) (quotient * suc (suc b)) (toℕ ys * suc (suc b))) ⟩
            Fin.toℕ remainder + quotient * suc (suc b) + toℕ ys * suc (suc b)
        ≡⟨ cong (λ w → w + toℕ ys * suc (suc b)) (sym property) ⟩
            suc (Fin.toℕ x + Fin.toℕ y + toℕ ys * suc (suc b))
        ≡⟨ cong suc (+-assoc (Fin.toℕ x) (Fin.toℕ y) (toℕ ys * suc (suc b))) ⟩
            suc (Fin.toℕ x + (Fin.toℕ y + toℕ ys * suc (suc b)))
        ≡⟨ sym (+-suc (Fin.toℕ x) (Fin.toℕ y + toℕ ys * suc (suc b))) ⟩
            Fin.toℕ x + suc (Fin.toℕ y + toℕ ys * suc (suc b))
        ≡⟨ cong (λ w → Fin.toℕ x + toℕ w) (sym eq) ⟩
            Fin.toℕ x + toℕ (fromℕ (toℕ (increase-base xs) * suc b))
        ≡⟨ cong (λ w → Fin.toℕ x + w) (toℕ-fromℕ (suc b) (toℕ (increase-base xs) * suc b)) ⟩
            Fin.toℕ x + toℕ (increase-base xs) * suc b
        ≡⟨ cong (λ w → Fin.toℕ x + w * suc b) (increase-base-lemma b xs) ⟩
            Fin.toℕ x + toℕ xs * suc b
        ∎

decrease-base-lemma : ∀ b → (xs : Bij (suc (suc b))) → toℕ (decrease-base xs) ≡ toℕ xs
decrease-base-lemma b ∙ = refl
decrease-base-lemma b (x ∷ xs) with fromℕ {b} (toℕ (decrease-base xs) * suc (suc b))
              | inspect (λ ws → fromℕ {b} (toℕ ws * suc (suc b))) (decrease-base xs)
decrease-base-lemma b (x ∷ xs) | ∙ | [ eq ] with full x
decrease-base-lemma b (x ∷ xs) | ∙ | [ eq ] | yes p = cong suc $
    begin
        suc (b + 0)
    ≡⟨ cong (λ w → w + 0) (sym p)⟩
        Fin.toℕ x + zero
    ≡⟨ cong (λ w → Fin.toℕ x + w) $
            begin
                zero
            ≡⟨ cong toℕ (sym eq) ⟩
                toℕ (fromℕ (toℕ (decrease-base xs) * suc (suc b)))
            ≡⟨ toℕ-fromℕ b (toℕ (decrease-base xs) * suc (suc b)) ⟩
                toℕ (decrease-base xs) * suc (suc b)
            ∎ ⟩
        Fin.toℕ x + toℕ (decrease-base xs) * suc (suc b)
    ≡⟨ cong (λ w → Fin.toℕ x + w * suc (suc b)) (decrease-base-lemma b xs) ⟩
        Fin.toℕ x + toℕ xs * suc (suc b)
    ∎
decrease-base-lemma b (x ∷ xs) | ∙ | [ eq ] | no ¬p = cong suc $
    begin
        Fin.toℕ (inject-1 x ¬p) + 0
    ≡⟨ cong (λ w → w + 0) (inject-1-lemma x ¬p) ⟩
        Fin.toℕ x + 0
    ≡⟨ cong (λ w → Fin.toℕ x + w) $
            begin
                zero
            ≡⟨ cong toℕ (sym eq) ⟩
                toℕ (fromℕ (toℕ (decrease-base xs) * suc (suc b)))
            ≡⟨ toℕ-fromℕ b (toℕ (decrease-base xs) * suc (suc b)) ⟩
                toℕ (decrease-base xs) * suc (suc b)
            ∎ ⟩
        Fin.toℕ x + toℕ (decrease-base xs) * suc (suc b)
    ≡⟨ cong (λ w → Fin.toℕ x + w * suc (suc b)) (decrease-base-lemma b xs) ⟩
        Fin.toℕ x + toℕ xs * suc (suc b)
    ∎
decrease-base-lemma b (x ∷ xs) | y ∷ ys | [ eq ] with (suc (Fin.toℕ x + Fin.toℕ y)) divMod (suc b)
decrease-base-lemma b (x ∷ xs) | y ∷ ys | [ eq ] | result quotient remainder property div-eq mod-eq
    rewrite div-eq | mod-eq = cong suc $ begin
            Fin.toℕ remainder + toℕ (n+ quotient ys) * suc b
        ≡⟨ cong (λ w → Fin.toℕ remainder + w * suc b) (toℕ-n+ b quotient ys) ⟩
            Fin.toℕ remainder + (quotient + toℕ ys) * suc b
        ≡⟨ cong (λ w → Fin.toℕ remainder + w) (distribʳ-*-+ (suc b) quotient (toℕ ys)) ⟩
            Fin.toℕ remainder + (quotient * suc b + toℕ ys * suc b)
        ≡⟨ sym (+-assoc (Fin.toℕ remainder) (quotient * suc b) (toℕ ys * suc b)) ⟩
            Fin.toℕ remainder + quotient * suc b + toℕ ys * suc b
        ≡⟨ cong (λ w → w + toℕ ys * suc b) (sym property) ⟩
            suc (Fin.toℕ x + Fin.toℕ y + toℕ ys * suc b)
        ≡⟨ cong suc (+-assoc (Fin.toℕ x) (Fin.toℕ y) (toℕ ys * suc b)) ⟩
            suc (Fin.toℕ x + (Fin.toℕ y + toℕ ys * suc b))
        ≡⟨ sym (+-suc (Fin.toℕ x) (Fin.toℕ y + toℕ ys * suc b)) ⟩
            Fin.toℕ x + suc (Fin.toℕ y + toℕ ys * suc b)
        ≡⟨ cong (λ w → Fin.toℕ x + toℕ w) (sym eq) ⟩
            Fin.toℕ x + toℕ (fromℕ (toℕ (decrease-base xs) * suc (suc b)))
        ≡⟨ cong (λ w → Fin.toℕ x + w) (toℕ-fromℕ b (toℕ (decrease-base xs) * suc (suc b))) ⟩
            Fin.toℕ x + toℕ (decrease-base xs) * suc (suc b)
        ≡⟨ cong (λ w → Fin.toℕ x + w * suc (suc b)) (decrease-base-lemma b xs) ⟩
            Fin.toℕ x + toℕ xs * suc (suc b)
        ∎

    -- begin
    --     {!   !}
    -- ≡⟨ {!   !} ⟩
    --     {!   !}
    -- ≡⟨ {!   !} ⟩
    --     {!   !}
    -- ≡⟨ {!   !} ⟩
    --     {!   !}
    -- ≡⟨ {!   !} ⟩
    --     {!   !}
    -- ∎


module Measure where

import Data.Nat              as ℕ
import Data.Nat.Coprimality  as Coprimality
import Data.Nat.GCD          as GCD
import Data.Nat.Show         as ℕshow
import Data.Nat.Divisibility as Divisibility

open ℕ            using (ℕ; suc; zero)
open Coprimality  using (Coprime)
open Divisibility using (divides)

open import Relation.Nullary.Decidable using (True; False; toWitness; fromWitness)
open import Data.Product using (_,_; ∃; proj₁; proj₂)
open import Data.String as String using (String)
open import Relation.Binary.PropositionalEquality as ≡ using (_≡_; _≢_; cong; sym; refl)

open import Data.Empty as ⊥ using (⊥)

record ℙ : Set where
  field
    numerator     : ℕ
    denominator-1 : ℕ
    .coprime      : Coprime numerator (suc denominator-1)

  denominator : ℕ
  denominator = suc denominator-1
open ℙ

infixl 7 _÷₌_
_÷₌_ : (n : ℕ) → (d : ℕ) → .{coprime : Coprime n d} → .{≢0 : False (d ℕ.≟ 0)} → ∃ (λ p → denominator p ≡ d)
numerator     (proj₁ (n ÷₌ _))                 = n
denominator-1 (proj₁ ((_ ÷₌ zero) {≢0 = ()}))
denominator-1 (proj₁ (_ ÷₌ suc d))             = d
coprime       (proj₁ ((_ ÷₌ zero) {≢0 = ()}))
coprime       (proj₁ ((_ ÷₌ suc d) {coprime})) = coprime
proj₂ ((n ÷₌ zero) {≢0 = ()})
proj₂ (n ÷₌ suc d) = refl

infixl 7 _÷_
_÷_ : (n d : ℕ) → .{coprime : Coprime n d} → .{≢0 : False (d ℕ.≟ 0)} → ℙ
numerator (n ÷ d) = n
denominator-1 ((n ÷ zero) {≢0 = ()})
denominator-1 (n ÷ suc d-1) = d-1
coprime ((n ÷ zero) {≢0 = ()})
coprime ((n ÷ suc d-1) {coprime}) = coprime

fromNat : ℕ → ℙ
numerator (fromNat n) = n
denominator-1 (fromNat _) = 0
coprime (fromNat n) = Coprimality.sym (Coprimality.1-coprimeTo n)

private  1+≢*0 : ∀ x y → suc x ≢ y ℕ.* 0
         1+≢*0 x zero ()
         1+≢*0 x (suc y) = 1+≢*0 x y

infixl 7 _?÷₌_
_?÷₌_ : ∀ x y → .{≢0 : False (y ℕ.≟ 0)} → ∃ (λ p → x ℕ.* denominator p ≡ numerator p ℕ.* y)
_?÷₌_ x zero {≢0 = ()}
_?÷₌_ x (suc y-1) with GCD.Bézout.lemma x (suc y-1)
_?÷₌_ x (suc y-1) | GCD.Bézout.result 0 (GCD.GCD.is (_ , divides y′ y-eq) _) _ = ⊥.⊥-elim (1+≢*0 y-1 y′ y-eq)
_?÷₌_ x (suc y-1) | GCD.Bézout.result (suc d-1) (GCD.GCD.is (divides x′ x-eq , divides y′ y-eq) _) bézout = proj₁ frac , eq-prf
  where
    open import Data.Nat using (_*_)

    y = suc y-1
    d = suc d-1

    bézout′ : GCD.Bézout.Identity d (x′ * d) (y′ * d)
    bézout′ = ≡.subst₂ (GCD.Bézout.Identity d) x-eq y-eq bézout

    coprime-prf : Coprime x′ y′
    coprime-prf = Coprimality.Bézout-coprime bézout′

    open ≡.≡-Reasoning
    open import Data.Nat.Properties.Simple

    y′≢0 : False (y′ ℕ.≟ 0)
    y′≢0 = Relation.Nullary.Decidable.fromWitnessFalse wtns
      where
      suc-≢0 : {n : ℕ} → (suc n ≡ 0) → ⊥
      suc-≢0 {n} ()

      open import Relation.Nullary using (¬_)
      wtns : ¬ (y′ ≡ 0)
      wtns refl = suc-≢0 {y-1} y-eq

    frac : ∃ (λ p → denominator p ≡ y′)
    frac = (x′ ÷₌ y′) {coprime = coprime-prf} {≢0 = y′≢0}

    eq-prf : x * denominator (proj₁ frac) ≡ numerator (proj₁ frac) * suc y-1
    eq-prf = begin
      x * denominator (proj₁ frac) ≡⟨ cong (λ z → x * z) (proj₂ frac) ⟩
      x * y′         ≡⟨ cong (λ z → z * y′) x-eq ⟩
      x′ * d * y′    ≡⟨ *-assoc x′ d y′ ⟩
      x′ * (d * y′)  ≡⟨ sym (cong (_*_ x′) (*-comm y′ d)) ⟩
      x′ * (y′ * d)  ≡⟨ sym (cong (_*_ x′) y-eq)  ⟩
      x′ * y ∎

infixl 7 _?÷_
_?÷_ : (x y : ℕ) → .{≢0 : False (y ℕ.≟ 0)} → ℙ
(x ?÷ zero) {≢0 = ()}
x ?÷ suc y-1 with GCD.Bézout.lemma x (suc y-1)
(n ?÷ suc y-1) | GCD.Bézout.result 0 (GCD.GCD.is (_ , divides y′ y-eq) _) _ = ⊥.⊥-elim (1+≢*0 y-1 y′ y-eq)
(n ?÷ suc y-1) | GCD.Bézout.result (suc d-1) (GCD.GCD.is (divides x′ x-eq , divides y′ y-eq) _) bézout = (x′ ÷ y′) {coprime = coprime-prf} {≢0 = y′≢0}
  where
  open import Data.Nat using (_*_)

  y = suc y-1
  d = suc d-1

  bézout′ : GCD.Bézout.Identity d (x′ * d) (y′ * d)
  bézout′ = ≡.subst₂ (GCD.Bézout.Identity d) x-eq y-eq bézout

  coprime-prf : Coprime x′ y′
  coprime-prf = Coprimality.Bézout-coprime bézout′

  y′≢0 : False (y′ ℕ.≟ 0)
  y′≢0 = Relation.Nullary.Decidable.fromWitnessFalse wtns
    where
    suc-≢0 : {n : ℕ} → (suc n ≡ 0) → ⊥
    suc-≢0 {n} ()

    open import Relation.Nullary using (¬_)
    wtns : ¬ (y′ ≡ 0)
    wtns refl = suc-≢0 {y-1} y-eq

_+_ : ℙ → ℙ → ℙ
x + y = ((numerator x ℕ.* denominator y) ℕ.+ (numerator y ℕ.* denominator x)) ?÷ (denominator x ℕ.* denominator y)

_*_ : ℙ → ℙ → ℙ
x * y = (numerator x ℕ.* numerator y) ?÷ (denominator x ℕ.* denominator y)

show : ℙ → String
show p = ℕshow (numerator p) ++ "÷" ++ ℕshow (denominator p)
  where
  open String using (_++_)
  open import Data.Nat.Show renaming (show to ℕshow)


{-# OPTIONS --allow-unsolved-metas #-}

module finiteSetUtil  where

open import Data.Nat hiding ( _≟_ )
open import Data.Fin renaming ( _<_ to _<<_ ; _>_ to _f>_ ; _≟_ to _f≟_ ) hiding (_≤_ )
open import Data.Fin.Properties hiding ( <-trans ) renaming ( <-cmp to <-fcmp )
open import Data.Empty
open import Relation.Nullary
open import Relation.Binary.Definitions
open import Relation.Binary.PropositionalEquality
open import logic
open import nat
open import finiteSet
open import fin
open import Data.Nat.Properties as NatP  hiding ( _≟_ )
open import Relation.Binary.HeterogeneousEquality as HE using (_≅_ )

record Found ( Q : Set ) (p : Q → Bool ) : Set where
     field
       found-q : Q
       found-p : p found-q ≡ true

open Bijection

open import Axiom.Extensionality.Propositional
open import Level hiding (suc ; zero)
postulate f-extensionality : { n : Level}  → Axiom.Extensionality.Propositional.Extensionality n n -- (Level.suc n)

module _ {Q : Set } (F : FiniteSet Q) where
     open FiniteSet F
     equal?-refl  : { x : Q } → equal? x x ≡ true
     equal?-refl {x} with F←Q x ≟ F←Q x
     ... | yes refl = refl
     ... | no ne = ⊥-elim (ne refl)
     equal→refl  : { x y : Q } → equal? x y ≡ true → x ≡ y
     equal→refl {q0} {q1} eq with F←Q q0 ≟ F←Q q1
     equal→refl {q0} {q1} refl | yes eq = begin
            q0
        ≡⟨ sym ( finiso→ q0) ⟩
            Q←F (F←Q q0)
        ≡⟨ cong (λ k → Q←F k ) eq ⟩
            Q←F (F←Q q1)
        ≡⟨  finiso→   q1 ⟩
            q1
        ∎  where open ≡-Reasoning
     eqP : (x y : Q) → Dec ( x ≡ y )
     eqP x y with F←Q x ≟ F←Q y
     ... | yes eq = yes (subst₂ (λ j k → j ≡ k ) (finiso→ x) (finiso→ y) (cong Q←F eq) )
     ... | no n = no (λ eq → n (cong F←Q eq))
     End : (m : ℕ ) → (p : Q → Bool ) → Set
     End m p = (i : Fin finite) → m ≤ toℕ i → p (Q←F i )  ≡ false
     first-end :  ( p : Q → Bool ) → End finite p
     first-end p i i>n = ⊥-elim (nat-≤> i>n (fin<n {finite} {i}) )
     next-end : {m : ℕ } → ( p : Q → Bool ) → End (suc m) p
              → (m<n : m < finite ) → p (Q←F (fromℕ< m<n )) ≡ false
              → End m p
     next-end {m} p prev m<n np i m<i with NatP.<-cmp m (toℕ i)
     next-end p prev m<n np i m<i | tri< a ¬b ¬c = prev i a
     next-end p prev m<n np i m<i | tri> ¬a ¬b c = ⊥-elim ( nat-≤> m<i c )
     next-end {m} p prev m<n np i m<i | tri≈ ¬a b ¬c = subst ( λ k → p (Q←F k) ≡ false) (m<n=i i b m<n ) np where
              m<n=i : {n : ℕ } (i : Fin n) {m : ℕ } → m ≡ (toℕ i) → (m<n : m < n )  → fromℕ< m<n ≡ i
              m<n=i i refl m<n = fromℕ<-toℕ i m<n
     found : { p : Q → Bool } → (q : Q ) → p q ≡ true → exists p ≡ true
     found {p} q pt = found1 finite  (NatP.≤-refl ) ( first-end p ) where
         found1 : (m : ℕ ) (m<n : m Data.Nat.≤ finite ) → ((i : Fin finite) → m ≤ toℕ i → p (Q←F i )  ≡ false ) →  exists1 m m<n p ≡ true
         found1 0 m<n end = ⊥-elim ( ¬-bool (subst (λ k → k ≡ false ) (cong (λ k → p k) (finiso→ q) ) (end (F←Q q) z≤n )) pt )
         found1 (suc m)  m<n end with bool-≡-? (p (Q←F (fromℕ< m<n))) true
         found1 (suc m)  m<n end | yes eq = subst (λ k → k \/ exists1 m (<to≤  m<n) p ≡ true ) (sym eq) (bool-or-4 {exists1 m (<to≤  m<n) p} )
         found1 (suc m)  m<n end | no np = begin
                 p (Q←F (fromℕ< m<n)) \/ exists1 m (<to≤  m<n) p
              ≡⟨ bool-or-1 (¬-bool-t np ) ⟩
                 exists1 m (<to≤  m<n) p
              ≡⟨ found1 m (<to≤  m<n) (next-end p end m<n (¬-bool-t np )) ⟩
                 true
              ∎  where open ≡-Reasoning
     not-found : { p : Q → Bool } → ( (q : Q ) → p q ≡ false ) → exists p ≡ false
     not-found {p} pn = not-found2 finite NatP.≤-refl where
         not-found2 : (m : ℕ ) → (m<n : m Data.Nat.≤ finite ) → exists1 m m<n p ≡ false
         not-found2  zero  _ = refl
         not-found2 ( suc m ) m<n with pn (Q←F (fromℕ< {m} {finite} m<n))
         not-found2 (suc m) m<n | eq = begin
                  p (Q←F (fromℕ< m<n)) \/ exists1 m (<to≤ m<n) p
              ≡⟨ bool-or-1 eq ⟩
                  exists1 m (<to≤ m<n) p
              ≡⟨ not-found2 m (<to≤ m<n)  ⟩
                  false
              ∎  where open ≡-Reasoning
     found← : { p : Q → Bool } → exists p ≡ true → Found Q p
     found← {p} exst = found2 finite NatP.≤-refl  (first-end p ) where
         found2 : (m : ℕ ) (m<n : m Data.Nat.≤ finite ) → End m p →  Found Q p
         found2 0 m<n end = ⊥-elim ( ¬-bool (not-found (λ q → end (F←Q q)  z≤n ) ) (subst (λ k → exists k ≡ true) (sym lemma) exst ) ) where
             lemma : (λ z → p (Q←F (F←Q z))) ≡ p
             lemma = f-extensionality ( λ q → subst (λ k → p k ≡ p q ) (sym (finiso→ q)) refl )
         found2 (suc m)  m<n end with bool-≡-? (p (Q←F (fromℕ< m<n))) true
         found2 (suc m)  m<n end | yes eq = record { found-q = Q←F (fromℕ< m<n) ; found-p = eq }
         found2 (suc m)  m<n end | no np =
               found2 m (<to≤ m<n) (next-end p end m<n (¬-bool-t np ))
     not-found← : { p : Q → Bool } → exists p ≡ false → (q : Q ) → p q ≡ false
     not-found← {p} np q = ¬-bool-t ( contra-position {_} {_} {_} {exists p ≡ true} (found q) (λ ep → ¬-bool np ep ) )

iso-fin : {A B : Set} → FiniteSet A  → Bijection A B → FiniteSet B
iso-fin {A} {B}  fin iso = record {
   Q←F = λ f → fun→ iso ( FiniteSet.Q←F fin f )
     ; F←Q = λ b → FiniteSet.F←Q fin (fun← iso b )
     ; finiso→ = finiso→
     ; finiso← = finiso←
   } where
   finiso→ : (q : B) → fun→ iso (FiniteSet.Q←F fin (FiniteSet.F←Q fin (Bijection.fun← iso q))) ≡ q
   finiso→ q = begin
             fun→ iso (FiniteSet.Q←F fin (FiniteSet.F←Q fin (Bijection.fun← iso q)))
           ≡⟨ cong (λ k → fun→ iso k ) (FiniteSet.finiso→ fin _ ) ⟩
             fun→ iso (Bijection.fun← iso q)
           ≡⟨ fiso→ iso _ ⟩
              q
           ∎  where open ≡-Reasoning
   finiso← : (f : Fin (FiniteSet.finite fin ))→ FiniteSet.F←Q fin (Bijection.fun← iso (Bijection.fun→ iso (FiniteSet.Q←F fin f))) ≡ f
   finiso← f = begin
              FiniteSet.F←Q fin (Bijection.fun← iso (Bijection.fun→ iso (FiniteSet.Q←F fin f)))
           ≡⟨ cong (λ k → FiniteSet.F←Q fin k ) (Bijection.fiso← iso _) ⟩
              FiniteSet.F←Q fin (FiniteSet.Q←F fin f)
           ≡⟨ FiniteSet.finiso← fin _  ⟩
              f
           ∎  where
           open ≡-Reasoning

data One : Set where
   one : One

fin-∨1 : {B : Set} → (fb : FiniteSet B ) → FiniteSet (One ∨ B)
fin-∨1 {B} fb =  record {
   Q←F = Q←F
 ; F←Q =  F←Q
 ; finiso→ = finiso→
 ; finiso← = finiso←
   }  where
   b = FiniteSet.finite fb
   Q←F : Fin (suc b) → One ∨ B
   Q←F zero = case1 one
   Q←F (suc f) = case2 (FiniteSet.Q←F fb f)
   F←Q : One ∨ B → Fin (suc b)
   F←Q (case1 one) = zero
   F←Q (case2 f ) = suc (FiniteSet.F←Q fb f)
   finiso→ : (q : One ∨ B) → Q←F (F←Q q) ≡ q
   finiso→ (case1 one) = refl
   finiso→ (case2 b) = cong (λ k → case2 k ) (FiniteSet.finiso→ fb b)
   finiso← : (q : Fin (suc b)) → F←Q (Q←F q) ≡ q
   finiso← zero = refl
   finiso← (suc f) = cong ( λ k → suc k ) (FiniteSet.finiso← fb f)

fin-∨2 : {B : Set} → ( a : ℕ ) → FiniteSet B  → FiniteSet (Fin a ∨ B)
fin-∨2 {B} zero  fb = iso-fin fb iso where
 iso : Bijection B (Fin zero ∨ B)
 iso =  record {
        fun← = fun←1
      ; fun→ = λ b → case2 b
      ; fiso→ = fiso→1
      ; fiso← = λ _ → refl
    } where
     fun←1 : Fin zero ∨ B → B
     fun←1 (case2 x) = x
     fiso→1 : (f : Fin zero ∨ B ) → case2 (fun←1 f) ≡ f
     fiso→1 (case2 x)  = refl
fin-∨2 {B} (suc a) fb =  iso-fin (fin-∨1 (fin-∨2 a fb) ) iso
    where
 iso : Bijection (One ∨ (Fin a ∨ B) ) (Fin (suc a) ∨ B)
 fun← iso (case1 zero) = case1 one
 fun← iso (case1 (suc f)) = case2 (case1 f)
 fun← iso (case2 b) = case2 (case2 b)
 fun→ iso (case1 one) = case1 zero
 fun→ iso (case2 (case1 f)) = case1 (suc f)
 fun→ iso (case2 (case2 b)) = case2 b
 fiso← iso (case1 one) = refl
 fiso← iso (case2 (case1 x)) = refl
 fiso← iso (case2 (case2 x)) = refl
 fiso→ iso (case1 zero) = refl
 fiso→ iso (case1 (suc x)) = refl
 fiso→ iso (case2 x) = refl

FiniteSet→Fin : {A : Set} → (fin : FiniteSet A  ) → Bijection (Fin (FiniteSet.finite fin)) A
fun← (FiniteSet→Fin fin) f = FiniteSet.F←Q fin f
fun→ (FiniteSet→Fin fin) f = FiniteSet.Q←F fin f
fiso← (FiniteSet→Fin fin) = FiniteSet.finiso← fin
fiso→ (FiniteSet→Fin fin) =  FiniteSet.finiso→ fin

fin-∨ : {A B : Set} → FiniteSet A → FiniteSet B → FiniteSet (A ∨ B)
fin-∨ {A} {B}  fa fb = iso-fin (fin-∨2 a  fb ) iso2 where
    a = FiniteSet.finite fa
    ia = FiniteSet→Fin fa
    iso2 : Bijection (Fin a ∨ B ) (A ∨ B)
    fun← iso2 (case1 x) = case1 (fun← ia x )
    fun← iso2 (case2 x) = case2 x
    fun→ iso2 (case1 x) = case1 (fun→ ia x )
    fun→ iso2 (case2 x) = case2 x
    fiso← iso2 (case1 x) = cong ( λ k → case1 k ) (Bijection.fiso← ia x)
    fiso← iso2 (case2 x) = refl
    fiso→ iso2 (case1 x) = cong ( λ k → case1 k ) (Bijection.fiso→ ia x)
    fiso→ iso2 (case2 x) = refl

open import Data.Product hiding ( map )

fin-× : {A B : Set} → FiniteSet A → FiniteSet B → FiniteSet (A × B)
fin-× {A} {B}  fa fb with FiniteSet→Fin fa
... | a=f = iso-fin (fin-×-f a ) iso-1 where
   a = FiniteSet.finite fa
   b = FiniteSet.finite fb
   iso-1 : Bijection (Fin a × B) ( A × B )
   fun← iso-1 x = ( FiniteSet.F←Q fa (proj₁ x)  , proj₂ x)
   fun→ iso-1 x = ( FiniteSet.Q←F fa (proj₁ x)  , proj₂ x)
   fiso← iso-1 x  =  lemma  where
     lemma : (FiniteSet.F←Q fa (FiniteSet.Q←F fa (proj₁ x)) , proj₂ x) ≡ ( proj₁ x , proj₂ x )
     lemma = cong ( λ k → ( k ,  proj₂ x ) )  (FiniteSet.finiso← fa _ )
   fiso→ iso-1 x = cong ( λ k → ( k ,  proj₂ x ) )  (FiniteSet.finiso→ fa _ )

   iso-2 : {a : ℕ } → Bijection (B ∨ (Fin a × B)) (Fin (suc a) × B)
   fun← iso-2 (zero , b ) = case1 b
   fun← iso-2 (suc fst , b ) = case2 ( fst , b )
   fun→ iso-2 (case1 b) = ( zero , b )
   fun→ iso-2 (case2 (a , b )) = ( suc a , b )
   fiso← iso-2 (case1 x) = refl
   fiso← iso-2 (case2 x) = refl
   fiso→ iso-2 (zero , b ) = refl
   fiso→ iso-2 (suc a , b ) = refl

   fin-×-f : ( a  : ℕ ) → FiniteSet ((Fin a) × B)
   fin-×-f zero = record { Q←F = λ () ; F←Q = λ () ; finiso→ = λ () ; finiso← = λ () ; finite = 0 }
   fin-×-f (suc a) = iso-fin ( fin-∨ fb ( fin-×-f a ) ) iso-2

open _∧_

fin-∧ : {A B : Set} → FiniteSet A → FiniteSet B → FiniteSet (A ∧ B)
fin-∧ {A} {B} fa fb with FiniteSet→Fin fa    -- same thing for our tool
... | a=f = iso-fin (fin-×-f a ) iso-1 where
   a = FiniteSet.finite fa
   b = FiniteSet.finite fb
   iso-1 : Bijection (Fin a ∧ B) ( A ∧ B )
   fun← iso-1 x = record { proj1 = FiniteSet.F←Q fa (proj1 x)  ; proj2 =  proj2 x}
   fun→ iso-1 x = record { proj1 = FiniteSet.Q←F fa (proj1 x)  ; proj2 =  proj2 x}
   fiso← iso-1 x  =  lemma  where
     lemma : record { proj1 = FiniteSet.F←Q fa (FiniteSet.Q←F fa (proj1 x)) ; proj2 =  proj2 x} ≡ record {proj1 =  proj1 x ; proj2 =  proj2 x }
     lemma = cong ( λ k → record {proj1 = k ;  proj2 = proj2 x } )  (FiniteSet.finiso← fa _ )
   fiso→ iso-1 x = cong ( λ k → record {proj1 =  k ; proj2 =  proj2 x } )  (FiniteSet.finiso→ fa _ )

   iso-2 : {a : ℕ } → Bijection (B ∨ (Fin a ∧ B)) (Fin (suc a) ∧ B)
   fun← iso-2 (record { proj1 = zero ; proj2 =  b }) = case1 b
   fun← iso-2 (record { proj1 = suc fst ; proj2 =  b }) = case2 ( record { proj1 = fst ; proj2 =  b } )
   fun→ iso-2 (case1 b) = record {proj1 =  zero ; proj2 =  b }
   fun→ iso-2 (case2 (record { proj1 = a ; proj2 =  b })) = record { proj1 =  suc a ; proj2 =  b }
   fiso← iso-2 (case1 x) = refl
   fiso← iso-2 (case2 x) = refl
   fiso→ iso-2 (record { proj1 = zero ; proj2 =  b }) = refl
   fiso→ iso-2 (record { proj1 = suc a ; proj2 =  b }) = refl

   fin-×-f : ( a  : ℕ ) → FiniteSet ((Fin a) ∧ B)
   fin-×-f zero = record { Q←F = λ () ; F←Q = λ () ; finiso→ = λ () ; finiso← = λ () ; finite = 0 }
   fin-×-f (suc a) = iso-fin ( fin-∨ fb ( fin-×-f a ) ) iso-2

-- import Data.Nat.DivMod

open import Data.Vec hiding ( map ; length )
import Data.Product

exp2 : (n : ℕ ) → exp 2 (suc n) ≡ exp 2 n Data.Nat.+ exp 2 n
exp2 n = begin
      exp 2 (suc n)
   ≡⟨⟩
      2 * ( exp 2 n )
   ≡⟨ *-comm 2 (exp 2 n)  ⟩
      ( exp 2 n ) * 2
   ≡⟨ *-suc ( exp 2 n ) 1 ⟩
      (exp 2 n ) Data.Nat.+ ( exp 2 n ) * 1
   ≡⟨ cong ( λ k →  (exp 2 n ) Data.Nat.+  k ) (proj₂ *-identity (exp 2 n) ) ⟩
      exp 2 n Data.Nat.+ exp 2 n
   ∎  where
       open ≡-Reasoning
       open Data.Product

cast-iso : {n m : ℕ } → (eq : n ≡ m ) → (f : Fin m ) → cast eq ( cast (sym eq ) f)  ≡ f
cast-iso refl zero =  refl
cast-iso refl (suc f) = cong ( λ k → suc k ) ( cast-iso refl f )

fin2List : {n : ℕ } → FiniteSet (Vec Bool n)
fin2List {zero} = record {
   Q←F = λ _ → Vec.[]
 ; F←Q =  λ _ → # 0
 ; finiso→ = finiso→
 ; finiso← = finiso←
   } where
   Q = Vec Bool zero
   finiso→ : (q : Q) → [] ≡ q
   finiso→ [] = refl
   finiso← : (f : Fin (exp 2 zero)) → # 0 ≡ f
   finiso← zero = refl
fin2List {suc n} = subst (λ k → FiniteSet (Vec Bool (suc n)) ) (sym (exp2 n)) ( iso-fin (fin-∨ (fin2List ) (fin2List )) iso )
    where
   QtoR : Vec Bool (suc  n) →  Vec Bool n ∨ Vec Bool n
   QtoR ( true ∷ x ) = case1 x
   QtoR ( false ∷ x ) = case2 x
   RtoQ : Vec Bool n ∨ Vec Bool n → Vec Bool (suc  n)
   RtoQ ( case1 x ) = true ∷ x
   RtoQ ( case2 x ) = false ∷ x
   isoRQ : (x : Vec Bool (suc  n) ) → RtoQ ( QtoR x ) ≡ x
   isoRQ (true ∷ _ ) = refl
   isoRQ (false ∷ _ ) = refl
   isoQR : (x : Vec Bool n ∨ Vec Bool n ) → QtoR ( RtoQ x ) ≡ x
   isoQR (case1 x) = refl
   isoQR (case2 x) = refl
   iso : Bijection (Vec Bool n ∨ Vec Bool n) (Vec Bool (suc n))
   iso = record { fun← = QtoR ; fun→ = RtoQ ; fiso← = isoQR ; fiso→ = isoRQ  }

F2L : {Q : Set } {n : ℕ } → (fin : FiniteSet Q ) → n < suc (FiniteSet.finite fin) → ( (q : Q) → toℕ (FiniteSet.F←Q fin q ) < n  → Bool ) → Vec Bool n
F2L  {Q} {zero} fin _ Q→B = []
F2L  {Q} {suc n} fin (s≤s n<m) Q→B = Q→B (FiniteSet.Q←F fin (fromℕ< n<m)) lemma6 ∷ F2L {Q} fin (NatP.<-trans n<m a<sa ) qb1 where
   lemma6 : toℕ (FiniteSet.F←Q fin (FiniteSet.Q←F fin (fromℕ< n<m))) < suc n
   lemma6 = subst (λ k → toℕ k < suc n ) (sym (FiniteSet.finiso← fin _ )) (subst (λ k → k < suc n) (sym (toℕ-fromℕ< n<m ))  a<sa )
   qb1 : (q : Q) → toℕ (FiniteSet.F←Q fin q) < n → Bool
   qb1 q q<n = Q→B q (NatP.<-trans q<n a<sa)

List2Func : { Q : Set } → {n : ℕ } → (fin : FiniteSet Q ) → n < suc (FiniteSet.finite fin)  → Vec Bool n →  Q → Bool
List2Func {Q} {zero} fin (s≤s z≤n) [] q = false
List2Func {Q} {suc n} fin (s≤s n<m) (h ∷ t) q with  FiniteSet.F←Q fin q ≟ fromℕ< n<m
... | yes _ = h
... | no _ = List2Func {Q} fin (NatP.<-trans n<m a<sa ) t q

open import Level renaming ( suc to Suc ; zero to Zero)
open import Axiom.Extensionality.Propositional
-- postulate f-extensionality : { n : Level}  →  Axiom.Extensionality.Propositional.Extensionality n n

F2L-iso : { Q : Set } → (fin : FiniteSet Q ) → (x : Vec Bool (FiniteSet.finite fin) ) → F2L fin a<sa (λ q _ → List2Func fin a<sa x q ) ≡ x
F2L-iso {Q} fin x = f2l m a<sa x where
  m = FiniteSet.finite fin
  f2l : (n : ℕ ) → (n<m : n < suc m )→ (x : Vec Bool n ) → F2L fin n<m (λ q q<n → List2Func fin n<m x q )  ≡ x
  f2l zero (s≤s z≤n) [] = refl
  f2l (suc n) (s≤s n<m) (h ∷ t ) = lemma1 lemma2 lemma3f where
    lemma1 : {n : ℕ } → {h h1 : Bool } → {t t1 : Vec Bool n } → h ≡ h1 → t ≡ t1 →  h ∷ t ≡ h1 ∷ t1
    lemma1 refl refl = refl
    lemma2 : List2Func fin (s≤s n<m) (h ∷ t) (FiniteSet.Q←F fin (fromℕ< n<m)) ≡ h
    lemma2 with FiniteSet.F←Q fin (FiniteSet.Q←F fin (fromℕ< n<m))  ≟ fromℕ< n<m
    lemma2 | yes p = refl
    lemma2 | no ¬p = ⊥-elim ( ¬p (FiniteSet.finiso← fin _) )
    lemma4 : (q : Q ) → toℕ (FiniteSet.F←Q fin q ) < n → List2Func fin (s≤s n<m) (h ∷ t) q ≡ List2Func fin (NatP.<-trans n<m a<sa) t q
    lemma4 q _ with FiniteSet.F←Q fin q ≟ fromℕ< n<m
    lemma4 q lt | yes p = ⊥-elim ( nat-≡< (toℕ-fromℕ< n<m) (lemma5 n lt (cong (λ k → toℕ k) p))) where
        lemma5 : {j k : ℕ } → ( n : ℕ) → suc j ≤ n → j ≡ k → k < n
        lemma5 {zero} (suc n) (s≤s z≤n) refl = s≤s  z≤n
        lemma5 {suc j} (suc n) (s≤s lt) refl = s≤s (lemma5 {j} n lt refl)
    lemma4 q _ | no ¬p = refl
    lemma3f :  F2L fin (NatP.<-trans n<m a<sa) (λ q q<n → List2Func fin (s≤s n<m) (h ∷ t) q  ) ≡ t
    lemma3f = begin
         F2L fin (NatP.<-trans n<m a<sa) (λ q q<n → List2Func fin (s≤s n<m) (h ∷ t) q )
       ≡⟨ cong (λ k → F2L fin (NatP.<-trans n<m a<sa) ( λ q q<n → k q q<n ))
              (f-extensionality ( λ q →
              (f-extensionality ( λ q<n →  lemma4 q q<n )))) ⟩
         F2L fin (NatP.<-trans n<m a<sa) (λ q q<n → List2Func fin (NatP.<-trans n<m a<sa) t q )
       ≡⟨ f2l n (NatP.<-trans n<m a<sa ) t ⟩
         t
       ∎  where
         open ≡-Reasoning

L2F : {Q : Set } {n : ℕ } → (fin : FiniteSet Q )  → n < suc (FiniteSet.finite fin) → Vec Bool n → (q :  Q ) → toℕ (FiniteSet.F←Q fin q ) < n  → Bool
L2F fin n<m x q q<n = List2Func fin n<m x q

L2F-iso : { Q : Set } → (fin : FiniteSet Q ) → (f : Q → Bool ) → (q : Q ) → (L2F fin a<sa (F2L fin a<sa (λ q _ → f q) )) q (toℕ<n _) ≡ f q
L2F-iso {Q} fin f q = l2f m a<sa (toℕ<n _) where
  m = FiniteSet.finite fin
  lemma11f : {n : ℕ } → (n<m : n < m )  → ¬ ( FiniteSet.F←Q fin q ≡ fromℕ< n<m ) → toℕ (FiniteSet.F←Q fin q) ≤ n → toℕ (FiniteSet.F←Q fin q) < n
  lemma11f  n<m ¬q=n q≤n = lemma13 n<m (contra-position (lemma12 n<m _) ¬q=n ) q≤n where
     lemma13 : {n nq : ℕ } → (n<m : n < m )  → ¬ ( nq ≡ n ) → nq  ≤ n → nq < n
     lemma13 {0} {0} (s≤s z≤n) nt z≤n = ⊥-elim ( nt refl )
     lemma13 {suc _} {0} (s≤s (s≤s n<m)) nt z≤n = s≤s z≤n
     lemma13 {suc n} {suc nq} n<m nt (s≤s nq≤n) = s≤s (lemma13 {n} {nq} (NatP.<-trans a<sa n<m ) (λ eq → nt ( cong ( λ k → suc k ) eq )) nq≤n)
     lemma3f : {a b : ℕ } → (lt : a < b ) → fromℕ< (s≤s lt) ≡ suc (fromℕ< lt)
     lemma3f (s≤s lt) = refl
     lemma12f : {n m : ℕ } → (n<m : n < m ) → (f : Fin m )  → toℕ f ≡ n → f ≡ fromℕ< n<m
     lemma12f {zero} {suc m} (s≤s z≤n) zero refl = refl
     lemma12f {suc n} {suc m} (s≤s n<m) (suc f) refl = subst ( λ k → suc f ≡ k ) (sym (lemma3f n<m) ) ( cong ( λ k → suc k ) ( lemma12f {n} {m} n<m f refl  ) )
  l2f :  (n : ℕ ) → (n<m : n < suc m ) → (q<n : toℕ (FiniteSet.F←Q fin q ) < n )  →  (L2F fin n<m (F2L fin n<m  (λ q _ → f q))) q q<n ≡ f q
  l2f zero (s≤s z≤n) ()
  l2f (suc n) (s≤s n<m) (s≤s n<q) with FiniteSet.F←Q fin q ≟ fromℕ< n<m
  l2f (suc n) (s≤s n<m) (s≤s n<q) | yes p = begin
        f (FiniteSet.Q←F fin (fromℕ< n<m))
     ≡⟨ cong ( λ k → f (FiniteSet.Q←F fin k )) (sym p)  ⟩
        f (FiniteSet.Q←F fin ( FiniteSet.F←Q fin q ))
     ≡⟨ cong ( λ k → f k ) (FiniteSet.finiso→ fin _ ) ⟩
        f q
     ∎  where
       open ≡-Reasoning
  l2f (suc n) (s≤s n<m) (s≤s n<q) | no ¬p = l2f n (NatP.<-trans n<m a<sa) (lemma11f n<m ¬p n<q)

fin→ : {A : Set} → FiniteSet A → FiniteSet (A → Bool )
fin→ {A}  fin = iso-fin fin2List iso where
    a = FiniteSet.finite fin
    iso : Bijection (Vec Bool a ) (A → Bool)
    fun← iso x = F2L fin a<sa ( λ q _ → x q )
    fun→ iso x = List2Func fin a<sa x
    fiso← iso x  =  F2L-iso fin x
    fiso→ iso x = lemma where
      lemma : List2Func fin a<sa (F2L fin a<sa (λ q _ → x q)) ≡ x
      lemma = f-extensionality ( λ q → L2F-iso fin x q )

Fin2Finite : ( n : ℕ ) → FiniteSet (Fin n)
Fin2Finite n = record { F←Q = λ x → x ; Q←F = λ x → x ; finiso← = λ q → refl ; finiso→ = λ q → refl }

data fin-less { n : ℕ } { A : Set }  (fa : FiniteSet A ) (n<m : n < FiniteSet.finite fa ) : Set where
  elm1 : (elm : A ) → toℕ (FiniteSet.F←Q fa elm ) < n → fin-less fa n<m

get-elm : { n : ℕ }  { A : Set } {fa : FiniteSet A } {n<m : n < FiniteSet.finite fa } → fin-less fa n<m → A
get-elm (elm1 a _ ) = a

get-< : { n : ℕ }  { A : Set } {fa : FiniteSet A } {n<m : n < FiniteSet.finite fa }→ (f : fin-less fa n<m ) → toℕ (FiniteSet.F←Q fa (get-elm f )) < n
get-< (elm1 _ b ) = b

fin-less-cong : { n : ℕ }  { A : Set } (fa : FiniteSet A ) (n<m : n < FiniteSet.finite fa )
   → (x y : fin-less fa n<m ) → get-elm {n} {A} {fa} x ≡ get-elm {n} {A} {fa} y → get-< x ≅  get-< y → x ≡ y
fin-less-cong fa n<m (elm1 elm x) (elm1 elm x) refl HE.refl = refl

fin-< : {A : Set} → { n : ℕ } → (fa : FiniteSet A ) → (n<m : n < FiniteSet.finite fa ) → FiniteSet (fin-less fa n<m )
fin-< {A}  {n} fa n<m = iso-fin (Fin2Finite n) iso where
    m = FiniteSet.finite fa
    iso : Bijection (Fin n) (fin-less fa n<m )
    lemma8f : {i j n : ℕ } → ( i ≡ j ) → {i<n : i < n } → {j<n : j < n } → i<n ≅ j<n
    lemma8f {zero} {zero} {suc n} refl {s≤s z≤n} {s≤s z≤n} = HE.refl
    lemma8f {suc i} {suc i} {suc n} refl {s≤s i<n} {s≤s j<n} = HE.cong (λ k → s≤s k ) ( lemma8f {i} {i}  refl  )
    lemma10f : {n i j  : ℕ } → ( i ≡ j ) → {i<n : i < n } → {j<n : j < n }  → fromℕ< i<n ≡ fromℕ< j<n
    lemma10f  refl  = HE.≅-to-≡ (HE.cong (λ k → fromℕ< k ) (lemma8f refl  ))
    lemma3f : {a b c : ℕ } → { a<b : a < b } { b<c : b < c } { a<c : a < c } → NatP.<-trans a<b b<c ≡ a<c
    lemma3f {a} {b} {c} {a<b} {b<c} {a<c} = HE.≅-to-≡ (lemma8f refl)
    lemma11f : {n : ℕ } {x : Fin n } → (n<m : n < m ) → toℕ (fromℕ< (NatP.<-trans (toℕ<n x) n<m)) ≡ toℕ x
    lemma11f {n} {x} n<m  = begin
         toℕ (fromℕ< (NatP.<-trans (toℕ<n x) n<m))
      ≡⟨ toℕ-fromℕ< _ ⟩
         toℕ x
      ∎  where
          open ≡-Reasoning
    fun← iso (elm1 elm x) = fromℕ< x
    fun→ iso x = elm1 (FiniteSet.Q←F fa (fromℕ< (NatP.<-trans x<n n<m ))) to<n where
      x<n : toℕ x < n
      x<n = toℕ<n x
      to<n : toℕ (FiniteSet.F←Q fa (FiniteSet.Q←F fa (fromℕ< (NatP.<-trans x<n n<m)))) < n
      to<n = subst (λ k → toℕ k < n ) (sym (FiniteSet.finiso← fa _ )) (subst (λ k → k < n ) (sym ( toℕ-fromℕ< (NatP.<-trans x<n n<m) )) x<n )
    fiso← iso x  = lemma2 where
      lemma2 : fromℕ< (subst (λ k → toℕ k < n) (sym
       (FiniteSet.finiso← fa (fromℕ< (NatP.<-trans (toℕ<n x) n<m)))) (subst (λ k → k < n)
       (sym (toℕ-fromℕ< (NatP.<-trans (toℕ<n x) n<m))) (toℕ<n x))) ≡ x
      lemma2 = begin
        fromℕ< (subst (λ k → toℕ k < n) (sym
          (FiniteSet.finiso← fa (fromℕ< (NatP.<-trans (toℕ<n x) n<m)))) (subst (λ k → k < n)
               (sym (toℕ-fromℕ< (NatP.<-trans (toℕ<n x) n<m))) (toℕ<n x)))
        ≡⟨⟩
           fromℕ< ( subst (λ k → toℕ ( k ) < n ) (sym (FiniteSet.finiso← fa _ )) lemma6 )
        ≡⟨ lemma10 (cong (λ k → toℕ k) (FiniteSet.finiso← fa _ ) ) ⟩
           fromℕ< lemma6
        ≡⟨ lemma10 (lemma11 n<m ) ⟩
           fromℕ< ( toℕ<n x )
        ≡⟨ fromℕ<-toℕ _ _ ⟩
           x
        ∎  where
          open ≡-Reasoning
          lemma6 : toℕ (fromℕ< (NatP.<-trans (toℕ<n x) n<m)) < n
          lemma6 = subst ( λ k → k < n ) (sym (toℕ-fromℕ< (NatP.<-trans (toℕ<n x) n<m))) (toℕ<n x )
    fiso→ iso (elm1 elm x) = fin-less-cong fa n<m _ _ lemma (lemma8 (cong (λ k → toℕ (FiniteSet.F←Q fa k) ) lemma ) ) where
      lemma13 : toℕ (fromℕ< x) ≡ toℕ (FiniteSet.F←Q fa elm)
      lemma13 = begin
            toℕ (fromℕ< x)
         ≡⟨ toℕ-fromℕ< _ ⟩
            toℕ (FiniteSet.F←Q fa elm)
         ∎  where open ≡-Reasoning
      lemma : FiniteSet.Q←F fa (fromℕ< (NatP.<-trans (toℕ<n (Bijection.fun← iso (elm1 elm x))) n<m)) ≡ elm
      lemma = begin
           FiniteSet.Q←F fa (fromℕ< (NatP.<-trans (toℕ<n (Bijection.fun← iso (elm1 elm x))) n<m))
         ≡⟨⟩
           FiniteSet.Q←F fa (fromℕ< ( NatP.<-trans (toℕ<n ( fromℕ< x ) ) n<m))
         ≡⟨ cong (λ k → FiniteSet.Q←F fa k) (lemma10 lemma13 ) ⟩
            FiniteSet.Q←F fa (fromℕ< ( NatP.<-trans x n<m))
         ≡⟨ cong (λ k → FiniteSet.Q←F fa (fromℕ< k )) (HE.≅-to-≡ (lemma8 refl)) ⟩
           FiniteSet.Q←F fa (fromℕ< ( toℕ<n (FiniteSet.F←Q fa elm)))
         ≡⟨ cong (λ k → FiniteSet.Q←F fa k ) ( fromℕ<-toℕ _ _ ) ⟩
           FiniteSet.Q←F fa (FiniteSet.F←Q fa elm )
         ≡⟨ FiniteSet.finiso→ fa _ ⟩
            elm
         ∎  where open ≡-Reasoning

open import Data.List

open FiniteSet

memberQ : { Q : Set }  (finq : FiniteSet Q) (q : Q) (qs : List Q ) → Bool
memberQ {Q} finq q [] = false
memberQ {Q} finq q (q0 ∷ qs) with equal? finq q q0
... | true = true
... | false = memberQ finq q qs

--
-- there is a duplicate element in finite list
--

phase2 : { Q : Set }  (finq : FiniteSet Q) (q : Q) (qs : List Q ) → Bool
phase2 finq q [] = false
phase2 finq q (x ∷ qs) with equal? finq q x
... | true = true
... | false = phase2 finq q qs
phase1 : { Q : Set }  (finq : FiniteSet Q) (q : Q) (qs : List Q ) → Bool
phase1 finq q [] = false
phase1 finq q (x ∷ qs) with equal? finq q x
... | true = phase2 finq q qs
... | false = phase1 finq q qs

dup-in-list : { Q : Set }  (finq : FiniteSet Q) (q : Q) (qs : List Q ) → Bool
dup-in-list {Q} finq q qs = phase1 finq q qs

--
-- if length of the list is longer than kinds of a finite set, there is a duplicate
-- prove this based on the theorem on Data.Fin
--

dup-in-list+fin : { Q : Set }  (finq : FiniteSet Q)
   → (q : Q) (qs : List Q )
   → fin-dup-in-list (F←Q  finq q) (map (F←Q finq) qs) ≡ true
   → dup-in-list finq q qs ≡ true
dup-in-list+fin {Q} finq q qs p = i-phase1 qs p where
    i-phase2 : (qs : List Q) →   fin-phase2 (F←Q  finq q) (map (F←Q finq) qs) ≡ true
        → phase2 finq q qs ≡ true
    i-phase2 (x ∷ qs) p with equal? finq q x | inspect (equal? finq q ) x | <-fcmp  (F←Q finq q)  (F←Q finq x)
    ... | true | _ | t = refl
    ... | false | _ | tri< a ¬b ¬c = i-phase2 qs p
    ... | false | record { eq = eq }  | tri≈ ¬a b ¬c = ⊥-elim (¬-bool eq
        (subst₂ (λ j k → equal? finq j k ≡ true) (finiso→ finq q) (subst (λ k →  Q←F finq k ≡ x) (sym b) (finiso→ finq x)) ( equal?-refl finq  )))
    ... | false | _ | tri> ¬a ¬b c = i-phase2 qs p
    i-phase1 : (qs : List Q) → fin-phase1 (F←Q  finq q) (map (F←Q finq) qs) ≡ true
        → phase1 finq q qs ≡ true
    i-phase1 (x ∷ qs) p with equal? finq q x |  inspect (equal? finq q ) x | <-fcmp  (F←Q finq q)  (F←Q finq x)
    ... | true | record { eq = eq }  | tri< a ¬b ¬c =  ⊥-elim ( nat-≡< (cong (λ x → toℕ (F←Q finq x)) ( equal→refl finq eq )) a )
    ... | true | _ | tri≈ ¬a b ¬c = i-phase2 qs p
    ... | true | record { eq = eq}  | tri> ¬a ¬b c = ⊥-elim ( nat-≡< (cong (λ x → toℕ (F←Q finq x)) (sym ( equal→refl finq eq ))) c )
    ... | false | _ | tri< a ¬b ¬c = i-phase1 qs p
    ... | false | record {eq = eq} | tri≈ ¬a b ¬c = ⊥-elim (¬-bool eq
        (subst₂ (λ j k → equal? finq j k ≡ true) (finiso→ finq q) (subst (λ k →  Q←F finq k ≡ x) (sym b) (finiso→ finq x)) ( equal?-refl finq  )))
    ... | false | _ | tri> ¬a ¬b c = i-phase1 qs p

record Dup-in-list {Q : Set } (finq : FiniteSet Q) (qs : List Q)  : Set where
   field
      dup : Q
      is-dup : dup-in-list finq dup qs ≡ true

dup-in-list>n : {Q : Set } → (finq : FiniteSet Q) → (qs : List Q)  → (len> : length qs > finite finq ) → Dup-in-list finq qs
dup-in-list>n {Q} finq qs lt = record { dup = Q←F finq (FDup-in-list.dup dl)
  ; is-dup = dup-in-list+fin finq (Q←F finq (FDup-in-list.dup dl)) qs dl01 } where
     maplen : (qs : List Q) → length (map (F←Q finq) qs) ≡ length qs
     maplen [] = refl
     maplen (x ∷ qs) = cong suc (maplen qs)
     dl : FDup-in-list (finite finq ) (map (F←Q finq) qs)
     dl = fin-dup-in-list>n (map (F←Q finq) qs) (subst (λ k → k > finite finq ) (sym (maplen qs)) lt)
     dl01 :  fin-dup-in-list (F←Q finq (Q←F finq (FDup-in-list.dup dl))) (map (F←Q finq) qs) ≡ true
     dl01 = subst (λ k →  fin-dup-in-list k (map (F←Q finq) qs) ≡ true )
         (sym (finiso← finq _)) ( FDup-in-list.is-dup dl )


open import Agda.Builtin.Nat
open import Agda.Builtin.Equality

record R : Set where
  field
    r1 r2 : Nat

test : (x : R) → R.r1 x ≡ 0 → Nat
test x refl = 0


------------------------------------------------------------------------
-- INCREMENTAL λ-CALCULUS
--
-- Change evaluation (Def 3.6 and Fig. 4h).
------------------------------------------------------------------------

import Parametric.Syntax.Type as Type
import Parametric.Syntax.Term as Term
import Parametric.Denotation.Value as Value
import Parametric.Denotation.Evaluation as Evaluation
import Parametric.Change.Validity as Validity

module Parametric.Change.Specification
    {Base : Type.Structure}
    (Const : Term.Structure Base)
    (⟦_⟧Base : Value.Structure Base)
    (⟦_⟧Const : Evaluation.Structure Const ⟦_⟧Base)
    {{validity-structure : Validity.Structure ⟦_⟧Base}}
  where

open Type.Structure Base
open Term.Structure Base Const
open Value.Structure Base ⟦_⟧Base
open Evaluation.Structure Const ⟦_⟧Base ⟦_⟧Const
open Validity.Structure ⟦_⟧Base {{validity-structure}}

open import Base.Denotation.Notation
open import Relation.Binary.PropositionalEquality
open import Theorem.CongApp
open import Postulate.Extensionality

module Structure where
  ---------------
  -- Interface --
  ---------------

  -- We provide: Derivatives of terms.
  ⟦_⟧Δ : ∀ {τ Γ} → (t : Term Γ τ) (ρ : ⟦ Γ ⟧) (dρ : Δ₍ Γ ₎ ρ) → Δ₍ τ ₎ (⟦ t ⟧ ρ)

  -- And we provide correctness proofs about the derivatives.
  correctness : ∀ {τ Γ} (t : Term Γ τ) →
    IsDerivative₍ Γ , τ ₎ ⟦ t ⟧ ⟦ t ⟧Δ

  --------------------
  -- Implementation --
  --------------------
  open import Base.Change.Equivalence
  open import Base.Change.Equivalence.Realizers

  module _ {σ τ : Type} {Γ : Context} (t : Term (σ • Γ) τ) where
    instance
      cΓ : ChangeAlgebra ⟦ Γ ⟧Context
      cΓ = change-algebra₍ Γ ₎
      cσ : ChangeAlgebra ⟦ σ ⟧Type
      cσ = change-algebra σ
      cτ : ChangeAlgebra ⟦ τ ⟧Type
      cτ = change-algebra τ

    private
      ⟦⟧Δλ = nil {{changeAlgebraFun {{cΓ}} {{change-algebra₍ σ ⇒ τ ₎}}}} ⟦ abs t ⟧

    ⟦⟧Δλ-realizer : ∀
      (ρ : ⟦ Γ ⟧) (dρ : Δ₍ Γ ₎ ρ)
      (v : ⟦ σ ⟧) (dv : Δ₍ σ ₎ v) →
      Δ₍ τ ₎ (⟦ t ⟧ (v • ρ))
    ⟦⟧Δλ-realizer ρ dρ v dv = ⟦ t ⟧Δ (v • ρ) (dv • dρ)

    ⟦⟧Δλ-realizer-correct :
      equiv-hp-binary {{cΓ}} {{cσ}} {{cτ}} (⟦ abs t ⟧) ⟦⟧Δλ ⟦⟧Δλ-realizer
    ⟦⟧Δλ-realizer-correct ρ dρ v dv =
      begin
        ⟦ t ⟧ (v ⊞₍ σ ₎ dv • ρ ⊞₍ Γ ₎ dρ) ⊟₍ τ ₎ ⟦ t ⟧ (v • ρ)
      ≙⟨ equiv-cancel-2 {{change-algebra τ}}
           (⟦ t ⟧ (v ⊞₍ σ ₎ dv • ρ ⊞₍ Γ ₎ dρ))
           (⟦ t ⟧Δ (v • ρ) (dv • dρ))
           (sym (correctness t (v • ρ) (dv • dρ)))
      ⟩
         ⟦ t ⟧Δ (v • ρ) (dv • dρ)
      ∎ where open ≙-Reasoning

    ⟦_⟧Δλ′-faster = proj₁ (equiv-raw-change-to-change-binary {{cΓ}} {{cσ}} {{cτ}} ⟦ abs t ⟧ ⟦⟧Δλ ⟦⟧Δλ-realizer ⟦⟧Δλ-realizer-correct)
      where
        open import Data.Product

  ⟦_⟧ΔVar : ∀ {τ Γ} → (x : Var Γ τ) → (ρ : ⟦ Γ ⟧) → Δ₍ Γ ₎ ρ → Δ₍ τ ₎ (⟦ x ⟧Var ρ)
  ⟦ this   ⟧ΔVar (v • ρ) (dv • dρ) = dv
  ⟦ that x ⟧ΔVar (v • ρ) (dv • dρ) = ⟦ x ⟧ΔVar ρ dρ

  ⟦_⟧Δ (const {τ} c) ρ dρ = nil₍ τ ₎ (⟦ c ⟧Const)
  ⟦_⟧Δ (var x) ρ dρ = ⟦ x ⟧ΔVar ρ dρ
  ⟦_⟧Δ (app {σ} {τ} s t) ρ dρ =
       call-change {σ} {τ} (⟦ s ⟧Δ ρ dρ) (⟦ t ⟧ ρ) (⟦ t ⟧Δ ρ dρ)
  ⟦_⟧Δ (abs {σ} {τ} t) ρ dρ = apply ⟦ t ⟧Δλ′-faster ρ dρ

  correctVar : ∀ {τ Γ} (x : Var Γ τ) →
    IsDerivative₍ Γ , τ ₎ ⟦ x ⟧ ⟦ x ⟧ΔVar
  correctVar (this) (v • ρ) (dv • dρ) = refl
  correctVar (that y) (v • ρ) (dv • dρ) = correctVar y ρ dρ

  correctness (const {τ} c) ρ dρ = update-nil₍ τ ₎ ⟦ c ⟧Const
  correctness {τ} (var x) ρ dρ = correctVar {τ} x ρ dρ
  correctness (app {σ} {τ} s t) ρ dρ =
    let
      f₁ = ⟦ s ⟧ ρ
      f₂ = ⟦ s ⟧ (after-env dρ)
      Δf = ⟦ s ⟧Δ ρ dρ
      u₁ = ⟦ t ⟧ ρ
      u₂ = ⟦ t ⟧ (after-env dρ)
      Δu = ⟦ t ⟧Δ ρ dρ
    in
      begin
        f₁ u₁ ⊞₍ τ ₎ call-change {σ} {τ} Δf u₁ Δu
      ≡⟨  sym (is-valid {σ} {τ} Δf u₁ Δu) ⟩
        (f₁ ⊞₍ σ ⇒ τ ₎ Δf) (u₁ ⊞₍ σ ₎ Δu)
      ≡⟨ correctness {σ ⇒ τ} s ρ dρ ⟨$⟩ correctness {σ} t ρ dρ ⟩
        f₂ u₂
      ∎ where open ≡-Reasoning
  correctness {σ ⇒ τ} {Γ} (abs t) ρ dρ = equiv-raw-deriv-to-deriv-binary {{change-algebra₍ Γ ₎}} {{change-algebra σ}} ⟦ abs t ⟧Term (⟦⟧Δλ-realizer t) (⟦⟧Δλ-realizer-correct t) ρ dρ

  -- Corollary: (f ⊞ df) (v ⊞ dv) = f v ⊞ df v dv

  corollary : ∀ {σ τ Γ}
    (s : Term Γ (σ ⇒ τ)) (t : Term Γ σ) (ρ : ⟦ Γ ⟧) (dρ : Δ₍ Γ ₎ ρ) →
      (⟦ s ⟧ ρ ⊞₍ σ ⇒ τ ₎ ⟦ s ⟧Δ ρ dρ)
        (⟦ t ⟧ ρ ⊞₍ σ ₎ ⟦ t ⟧Δ ρ dρ)
    ≡ (⟦ s ⟧ ρ) (⟦ t ⟧ ρ)
      ⊞₍ τ ₎
      (call-change {σ} {τ} (⟦ s ⟧Δ ρ dρ)) (⟦ t ⟧ ρ) (⟦ t ⟧Δ ρ dρ)

  corollary {σ} {τ} s t ρ dρ =
    is-valid {σ} {τ} (⟦ s ⟧Δ ρ dρ) (⟦ t ⟧ ρ) (⟦ t ⟧Δ ρ dρ)

  corollary-closed : ∀ {σ τ} (t : Term ∅ (σ ⇒ τ))
    (v : ⟦ σ ⟧) (dv : Δ₍ σ ₎ v)
    → ⟦ t ⟧ ∅ (after₍ σ ₎ dv)
    ≡ ⟦ t ⟧ ∅ v ⊞₍ τ ₎ call-change {σ} {τ} (⟦ t ⟧Δ ∅ ∅) v dv

  corollary-closed {σ} {τ} t v dv =
    let
      f  = ⟦ t ⟧ ∅
      Δf = ⟦ t ⟧Δ ∅ ∅
    in
      begin
        f (after₍ σ ₎ dv)
      ≡⟨ cong (λ hole → hole (after₍ σ ₎ dv))
              (sym (correctness {σ ⇒ τ} t ∅ ∅)) ⟩
         (f ⊞₍ σ ⇒ τ ₎ Δf) (after₍ σ ₎ dv)
      ≡⟨ is-valid {σ} {τ} (⟦ t ⟧Δ ∅ ∅) v dv ⟩
        f (before₍ σ ₎ dv) ⊞₍ τ ₎ call-change {σ} {τ} Δf v dv
      ∎ where open ≡-Reasoning


{-

Descriptor language for easily defining structures

-}
{-# OPTIONS --cubical --no-import-sorts --no-exact-split --safe #-}
module Cubical.Structures.Macro where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.SIP
open import Cubical.Functions.FunExtEquiv
open import Cubical.Data.Sigma
open import Cubical.Data.Maybe

open import Cubical.Structures.Constant
open import Cubical.Structures.Maybe
open import Cubical.Structures.NAryOp
open import Cubical.Structures.Parameterized
open import Cubical.Structures.Pointed
open import Cubical.Structures.Product
open import Cubical.Structures.Functorial

data FuncDesc (ℓ : Level) : Typeω where
  -- constant structure: X ↦ A
  constant : ∀ {ℓ'} → Type ℓ' → FuncDesc ℓ
  -- pointed structure: X ↦ X
  var : FuncDesc ℓ
  -- join of structures S,T : X ↦ (S X × T X)
  _,_ : FuncDesc ℓ  → FuncDesc ℓ  → FuncDesc ℓ
  -- structure S parameterized by constant A : X ↦ (A → S X)
  param : ∀ {ℓ'} → (A : Type ℓ') → FuncDesc ℓ  → FuncDesc ℓ
  -- structure S parameterized by variable argument: X ↦ (X → S X)
  maybe : FuncDesc ℓ → FuncDesc ℓ

data Desc (ℓ : Level) : Typeω where
  -- constant structure: X ↦ A
  constant : ∀ {ℓ'} → Type ℓ' → Desc ℓ
  -- pointed structure: X ↦ X
  var : Desc ℓ
  -- join of structures S,T : X ↦ (S X × T X)
  _,_ : Desc ℓ  → Desc ℓ  → Desc ℓ
  -- structure S parameterized by constant A : X ↦ (A → S X)
  param : ∀ {ℓ'} → (A : Type ℓ') → Desc ℓ  → Desc ℓ
  -- structure S parameterized by variable argument: X ↦ (X → S X)
  recvar : Desc ℓ  → Desc ℓ
  -- Maybe on a structure S: X ↦ Maybe (S X)
  maybe : Desc ℓ → Desc ℓ
  -- SNS from functorial action
  functorial : FuncDesc ℓ → Desc ℓ
  -- arbitrary standard notion of structure
  foreign : ∀ {ℓ' ℓ''} {S : Type ℓ → Type ℓ'} (ι : StrEquiv S ℓ'') → UnivalentStr S ι → Desc ℓ

infixr 4 _,_

{- Functorial structures -}

funcMacroLevel : ∀ {ℓ} → FuncDesc ℓ → Level
funcMacroLevel (constant {ℓ'} x) = ℓ'
funcMacroLevel {ℓ} var = ℓ
funcMacroLevel {ℓ} (d₀ , d₁) = ℓ-max (funcMacroLevel d₀) (funcMacroLevel d₁)
funcMacroLevel (param {ℓ'} A d) = ℓ-max ℓ' (funcMacroLevel d)
funcMacroLevel (maybe d) = funcMacroLevel d

-- Structure defined by a functorial descriptor
FuncMacroStructure : ∀ {ℓ} (d : FuncDesc ℓ) → Type ℓ → Type (funcMacroLevel d)
FuncMacroStructure (constant A) X = A
FuncMacroStructure var X = X
FuncMacroStructure (d₀ , d₁) X = FuncMacroStructure d₀ X × FuncMacroStructure d₁ X
FuncMacroStructure (param A d) X = A → FuncMacroStructure d X
FuncMacroStructure (maybe d) = MaybeStructure (FuncMacroStructure d)

-- Action defined by a functorial descriptor
funcMacroAction : ∀ {ℓ} (d : FuncDesc ℓ)
  {X Y : Type ℓ} → (X → Y) → FuncMacroStructure d X → FuncMacroStructure d Y
funcMacroAction (constant A) _ = idfun A
funcMacroAction var f = f
funcMacroAction (d₀ , d₁) f (s₀ , s₁) = funcMacroAction d₀ f s₀ , funcMacroAction d₁ f s₁
funcMacroAction (param A d) f s a = funcMacroAction d f (s a)
funcMacroAction (maybe d) f = map-Maybe (funcMacroAction d f)

-- Proof that the action preserves the identity
funcMacroId : ∀ {ℓ} (d : FuncDesc ℓ)
  {X : Type ℓ} → ∀ s → funcMacroAction d (idfun X) s ≡ s
funcMacroId (constant A) _ = refl
funcMacroId var _ = refl
funcMacroId (d₀ , d₁) (s₀ , s₁) = ΣPath≃PathΣ .fst (funcMacroId d₀ s₀ , funcMacroId d₁ s₁)
funcMacroId (param A d) s = funExt λ a → funcMacroId d (s a)
funcMacroId (maybe d) s = cong₂ map-Maybe (funExt (funcMacroId d)) refl ∙ map-Maybe-id s

{- General structures -}

macroStrLevel : ∀ {ℓ} → Desc ℓ → Level
macroStrLevel (constant {ℓ'} x) = ℓ'
macroStrLevel {ℓ} var = ℓ
macroStrLevel {ℓ} (d₀ , d₁) = ℓ-max (macroStrLevel d₀) (macroStrLevel d₁)
macroStrLevel (param {ℓ'} A d) = ℓ-max ℓ' (macroStrLevel d)
macroStrLevel {ℓ} (recvar d) = ℓ-max ℓ (macroStrLevel d)
macroStrLevel (maybe d) = macroStrLevel d
macroStrLevel (functorial d) = funcMacroLevel d
macroStrLevel (foreign {ℓ'} _ _) = ℓ'

macroEquivLevel : ∀ {ℓ} → Desc ℓ → Level
macroEquivLevel (constant {ℓ'} x) = ℓ'
macroEquivLevel {ℓ} var = ℓ
macroEquivLevel {ℓ} (d₀ , d₁) = ℓ-max (macroEquivLevel d₀) (macroEquivLevel d₁)
macroEquivLevel (param {ℓ'} A d) = ℓ-max ℓ' (macroEquivLevel d)
macroEquivLevel {ℓ} (recvar d) = ℓ-max ℓ (macroEquivLevel d)
macroEquivLevel (maybe d) = macroEquivLevel d
macroEquivLevel (functorial d) = funcMacroLevel d
macroEquivLevel (foreign {ℓ'' = ℓ''} _ _) = ℓ''

-- Structure defined by a descriptor
MacroStructure : ∀ {ℓ} (d : Desc ℓ) → Type ℓ → Type (macroStrLevel d)
MacroStructure (constant A) X = A
MacroStructure var X = X
MacroStructure (d₀ , d₁) X = MacroStructure d₀ X × MacroStructure d₁ X
MacroStructure (param A d) X = A → MacroStructure d X
MacroStructure (recvar d) X = X → MacroStructure d X
MacroStructure (maybe d) = MaybeStructure (MacroStructure d)
MacroStructure (functorial d) = FuncMacroStructure d
MacroStructure (foreign {S = S} _ _) = S

-- Notion of structured equivalence defined by a descriptor
MacroEquivStr : ∀ {ℓ} → (d : Desc ℓ) → StrEquiv {ℓ} (MacroStructure d) (macroEquivLevel d)
MacroEquivStr (constant A) = ConstantEquivStr A
MacroEquivStr var = PointedEquivStr
MacroEquivStr (d₀ , d₁) = ProductEquivStr (MacroEquivStr d₀) (MacroEquivStr d₁)
MacroEquivStr (param A d) = ParamEquivStr A λ _ → MacroEquivStr d
MacroEquivStr (recvar d) = UnaryFunEquivStr (MacroEquivStr d)
MacroEquivStr (maybe d) = MaybeEquivStr (MacroEquivStr d)
MacroEquivStr (functorial d) = FunctorialEquivStr (funcMacroAction d)
MacroEquivStr (foreign ι _) = ι

-- Proof that structure induced by descriptor is univalent
MacroUnivalentStr : ∀ {ℓ} → (d : Desc ℓ) → UnivalentStr (MacroStructure d) (MacroEquivStr d)
MacroUnivalentStr (constant A) = constantUnivalentStr A
MacroUnivalentStr var = pointedUnivalentStr
MacroUnivalentStr (d₀ , d₁) =
  ProductUnivalentStr (MacroEquivStr d₀) (MacroUnivalentStr d₀) (MacroEquivStr d₁) (MacroUnivalentStr d₁)
MacroUnivalentStr (param A d) = ParamUnivalentStr A (λ _ → MacroEquivStr d) (λ _ → MacroUnivalentStr d)
MacroUnivalentStr (recvar d) = unaryFunUnivalentStr (MacroEquivStr d) (MacroUnivalentStr d)
MacroUnivalentStr (maybe d) = maybeUnivalentStr (MacroEquivStr d) (MacroUnivalentStr d)
MacroUnivalentStr (functorial d) = functorialUnivalentStr (funcMacroAction d) (funcMacroId d)
MacroUnivalentStr (foreign _ θ) = θ

-- Module for easy importing
module Macro ℓ (d : Desc ℓ) where

  structure = MacroStructure d
  equiv = MacroEquivStr d
  univalent = MacroUnivalentStr d


------------------------------------------------------------------------
-- This file contains the definition of a preorder as a category with --
-- additional properties.                                             --
------------------------------------------------------------------------
module Category.Preorder where

open import Level renaming (suc to lsuc)
open import Data.Empty

open import Setoid.Total
open import Category.Category
open import Equality.Eq

record PO {l : Level} : Set (lsuc l) where
  field
    -- The underlying category.
    ℙ : Cat {l}

    -- The preorder axiom.
    POax : ∀{A B}{f g : el (Hom ℙ A B)} → ⟨ Hom ℙ A B ⟩[ f ≡ g ]

open PO public renaming (ℙ to po-cat)

-- A PO with 4 objects.
module 4PO where
    -- The objects.
    data 4Obj {l : Level} : Set l where
      i₁ : 4Obj
      i₂ : 4Obj
      i₃ : 4Obj
      i₄ : 4Obj

    -- The PreHom.
    data 4PHom {l : Level} : 4Obj {l} → 4Obj {l} → Set l where
      id₁ : 4PHom i₁ i₁
      f₁ : 4PHom i₁ i₂
      f₂ : 4PHom i₁ i₃
      id₂ : 4PHom i₂ i₂
      f₃ : 4PHom i₂ i₄
      id₃ : 4PHom i₃ i₃
      f₄ : 4PHom i₃ i₄
      f₅ : 4PHom i₁ i₄
      id₄ : 4PHom i₄ i₄

    -- The Hom.
    4Hom : {l : Level} → 4Obj {l} → 4Obj {l} → Setoid {l}
    4Hom i₁ i₁ = record { el = 4PHom i₁ i₁; eq = λ a b → a ≅ b; eqRpf = isEqRel }
    4Hom i₁ i₂ = record { el = 4PHom i₁ i₂; eq = λ a b → a ≅ b; eqRpf = isEqRel }
    4Hom i₁ i₃ = record { el = 4PHom i₁ i₃; eq = λ a b → a ≅ b; eqRpf = isEqRel }
    4Hom i₁ i₄ = record { el = 4PHom i₁ i₄; eq = λ a b → a ≅ b; eqRpf = isEqRel }
    4Hom i₂ i₂ = record { el = 4PHom i₂ i₂; eq = λ a b → a ≅ b; eqRpf = isEqRel }
    4Hom i₂ i₄ = record { el = 4PHom i₂ i₄; eq = λ a b → a ≅ b; eqRpf = isEqRel }
    4Hom i₃ i₃ = record { el = 4PHom i₃ i₃; eq = λ a b → a ≅ b; eqRpf = isEqRel }
    4Hom i₃ i₄ = record { el = 4PHom i₃ i₄; eq = λ a b → a ≅ b; eqRpf = isEqRel }
    4Hom i₄ i₄ = record { el = 4PHom i₄ i₄; eq = λ a b → a ≅ b; eqRpf = isEqRel }
    4Hom  _  _ = EmptySetoid

    4Comp : {l : Level}{a b c : 4Obj {l}} → BinSetoidFun (4Hom a b) (4Hom b c) (4Hom a c)
    4Comp {_} {i₁} {i₂} {i₁} = record { appT = λ x → record { appT = λ x₁ → id₁; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₂} {i₁} {i₁} = record { appT = λ x → record { appT = λ x₁ → x; extT = λ x₂ → refl }; extT = λ x₁ x₂ → x₁ }
    4Comp {_} {i₂} {i₁} {i₂} = record { appT = λ x → record { appT = λ x₁ → id₂; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₂} {i₁} {i₃} = record { appT = λ x → record { appT = λ x₁ → x; extT = λ x₂ → refl }; extT = λ x₁ x₂ → x₁ }
    4Comp {_} {i₂} {i₁} {i₄} = record { appT = λ x → record { appT = λ x₁ → f₃; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₃} {i₁} {i₁} = record { appT = λ x → record { appT = λ x₁ → x; extT = λ x₂ → refl }; extT = λ x₁ x₂ → x₁ }
    4Comp {_} {i₃} {i₂} {i₁} = record { appT = λ x → record { appT = λ x₁ → x₁; extT = λ x₂ → x₂ }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₃} {i₁} {i₂} = record { appT = λ x → record { appT = λ x₁ → x; extT = λ x₂ → refl }; extT = λ x₁ x₂ → x₁ }
    4Comp {_} {i₃} {i₁} {i₃} = record { appT = λ x → record { appT = λ x₁ → id₃; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₃} {i₁} {i₄} = record { appT = λ x → record { appT = λ x₁ → f₄; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₄} {i₁} {i₁} = record { appT = λ x → record { appT = λ x₁ → x; extT = λ x₂ → refl }; extT = λ x₁ x₂ → x₁ }
    4Comp {_} {i₄} {i₂} {i₁} = record { appT = λ x → record { appT = λ x₁ → x; extT = λ x₂ → refl }; extT = λ x₁ x₂ → x₁ }
    4Comp {_} {i₄} {i₁} {i₂} = record { appT = λ x → record { appT = λ x₁ → x; extT = λ x₂ → refl }; extT = λ x₁ x₂ → x₁ }
    4Comp {_} {i₄} {i₁} {i₃} = record { appT = λ x → record { appT = λ x₁ → x; extT = λ x₂ → refl }; extT = λ x₁ x₂ → x₁ }
    4Comp {_} {i₄} {i₃} {i₃} = record { appT = λ x → record { appT = λ x₁ → x; extT = λ x₂ → refl }; extT = λ x₁ x₂ → x₁ }
    4Comp {_} {i₄} {i₁} {i₄} = record { appT = λ x → record { appT = λ x₁ → ⊥-poly-elim x; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₄} {i₄} {i₃} = record { appT = λ x → record { appT = λ x₁ → x₁; extT = λ x₂ → x₂ }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₄} {i₄} {i₂} = record { appT = λ x → record { appT = λ x₁ → x₁; extT = λ x₂ → x₂ }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₄} {i₄} {i₁} = record { appT = λ x → record { appT = λ x₁ → x₁; extT = λ x₂ → x₂ }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₄} {i₃} {i₄} = record { appT = λ x → record { appT = λ x₁ → id₄; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₄} {i₃} {i₂} = record { appT = λ x → record { appT = λ x₁ → x; extT = λ x₂ → refl }; extT = λ x₁ x₂ → x₁ }
    4Comp {_} {i₄} {i₃} {i₁} = record { appT = λ x → record { appT = λ x₁ → x; extT = λ x₂ → refl }; extT = λ x₁ x₂ → x₁ }
    4Comp {_} {i₄} {i₂} {i₄} = record { appT = λ x → record { appT = λ x₁ → id₄; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₄} {i₂} {i₃} = record { appT = λ x → record { appT = λ x₁ → x; extT = λ x₂ → refl }; extT = λ x₁ x₂ → x₁ }
    4Comp {_} {i₄} {i₂} {i₂} = record { appT = λ x → record { appT = λ x₁ → x; extT = λ x₂ → refl }; extT = λ x₁ x₂ → x₁ }
    4Comp {_} {i₃} {i₄} {i₃} = record { appT = λ x → record { appT = λ x₁ → id₃; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₃} {i₄} {i₂} = record { appT = λ x → record { appT = λ x₁ → x₁; extT = λ x₂ → x₂ }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₃} {i₄} {i₁} = record { appT = λ x → record { appT = λ x₁ → x₁; extT = λ x₂ → x₂ }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₃} {i₃} {i₄} = record { appT = λ x → record { appT = λ x₁ → f₄; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₃} {i₃} {i₂} = record { appT = λ x → record { appT = λ x₁ → x₁; extT = λ x₂ → x₂ }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₃} {i₃} {i₁} = record { appT = λ x → record { appT = λ x₁ → x₁; extT = λ x₂ → x₂ }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₃} {i₂} {i₄} = record { appT = λ x → record { appT = λ x₁ → f₄; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₃} {i₂} {i₃} = record { appT = λ x → record { appT = λ x₁ → id₃; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₃} {i₂} {i₂} = record { appT = λ x → record { appT = λ x₁ → x; extT = λ x₂ → refl }; extT = λ x₁ x₂ → x₁ }
    4Comp {_} {i₃} {i₄} {i₄} = record { appT = λ x → record { appT = λ x₁ → f₄; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₂} {i₄} {i₃} = record { appT = λ x → record { appT = λ x₁ → x₁; extT = λ x₂ → x₂ }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₂} {i₄} {i₂} = record { appT = λ x → record { appT = λ x₁ → id₂; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₂} {i₄} {i₁} = record { appT = λ x → record { appT = λ x₁ → x₁; extT = λ x₂ → x₂ }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₂} {i₃} {i₄} = record { appT = λ x → record { appT = λ x₁ → f₃; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₂} {i₃} {i₂} = record { appT = λ x → record { appT = λ x₁ → id₂; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₂} {i₃} {i₁} = record { appT = λ x → record { appT = λ x₁ → x; extT = λ x₂ → refl }; extT = λ x₁ x₂ → x₁ }
    4Comp {_} {i₂} {i₂} {i₄} = record { appT = λ x → record { appT = λ x₁ → x₁; extT = λ x₂ → x₂ }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₂} {i₂} {i₃} = record { appT = λ x → record { appT = λ x₁ → x₁; extT = λ x₂ → x₂ }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₂} {i₂} {i₂} = record { appT = λ x → record { appT = λ x₁ → id₂; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₂} {i₃} {i₃} = record { appT = λ x → record { appT = λ x₁ → x; extT = λ x₂ → refl }; extT = λ x₁ x₂ → x₁ }
    4Comp {_} {i₂} {i₄} {i₄} = record { appT = λ x → record { appT = λ x₁ → x; extT = λ x₂ → refl }; extT = λ x₁ x₂ → x₁ }
    4Comp {_} {i₁} {i₄} {i₃} = record { appT = λ x → record { appT = λ x₁ → f₂; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₁} {i₄} {i₂} = record { appT = λ x → record { appT = λ x₁ → f₁; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₁} {i₄} {i₁} = record { appT = λ x → record { appT = λ x₁ → id₁; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₁} {i₃} {i₄} = record { appT = λ x → record { appT = λ x₁ → f₅; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₁} {i₃} {i₂} = record { appT = λ x → record { appT = λ x₁ → f₁; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₁} {i₃} {i₁} = record { appT = λ x → record { appT = λ x₁ → id₁; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₁} {i₂} {i₄} = record { appT = λ x → record { appT = λ x₁ → f₅; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₁} {i₂} {i₃} = record { appT = λ x → record { appT = λ x₁ → f₂; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₁} {i₂} {i₂} = record { appT = λ x → record { appT = λ x₁ → f₁; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₁} {i₃} {i₃} = record { appT = λ x → record { appT = λ x₁ → f₂; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {_} {i₁} {i₄} {i₄} = record { appT = λ x → record { appT = λ x₁ → f₅; extT = λ x₂ → refl }; extT = λ x₁ x₂ → refl }
    4Comp {l} {i₁} {i₁} {c}  = record { appT = λ x → record { appT = λ x₁ → x₁; extT = λ x₂ → x₂ }; extT = λ x₁ x₂ → refPf (eqRpf (4Hom i₁ c)) {x₂} }
    4Comp {l} {i₂} {i₂} {c}  = record { appT = λ x → record { appT = λ x₁ → x₁; extT = λ x₂ → x₂ }; extT = λ x₁ x₂ → refPf (eqRpf (4Hom i₂ c)) {x₂} }
    4Comp {l} {i₃} {i₃} {c}  = record { appT = λ x → record { appT = λ x₁ → x₁; extT = λ x₂ → x₂ }; extT = λ x₁ x₂ → refPf (eqRpf (4Hom i₃ c)) {x₂} }
    4Comp {l} {i₄} {i₄} {c}  = record { appT = λ x → record { appT = λ x₁ → x₁; extT = λ x₂ → x₂ }; extT = λ x₁ x₂ → refPf (eqRpf (4Hom i₄ c)) {x₂} }

    4Id : {l : Level}{A : 4Obj {l}} → el (4Hom A A)
    4Id {_}{i₁} = id₁
    4Id {_}{i₂} = id₂
    4Id {_}{i₃} = id₃
    4Id {_}{i₄} = id₄

    4PO-ax : ∀{l}{A B : 4Obj {l}}{f g : el (4Hom A B)} → eq (4Hom A B) f g
    4PO-ax {A = i₁} {i₁} {id₁} {id₁} = refl
    4PO-ax {A = i₁} {i₂} {f₁}  {f₁}  = refl
    4PO-ax {A = i₁} {i₃} {f₂}  {f₂}  = refl
    4PO-ax {A = i₁} {i₄} {f₅}  {f₅}  = refl
    4PO-ax {A = i₂} {i₂} {id₂} {id₂} = refl
    4PO-ax {A = i₂} {i₄} {f₃}  {f₃}  = refl
    4PO-ax {A = i₃} {i₃} {id₃} {id₃} = refl
    4PO-ax {A = i₃} {i₄} {f₄}  {f₄}  = refl
    4PO-ax {A = i₄} {i₄} {id₄} {id₄} = refl
    4PO-ax {A = i₂} {i₁} {f}         = ⊥-poly-elim f
    4PO-ax {A = i₃} {i₁} {f}         = ⊥-poly-elim f
    4PO-ax {A = i₄} {i₁} {f}         = ⊥-poly-elim f
    4PO-ax {A = i₃} {i₂} {f}         = ⊥-poly-elim f
    4PO-ax {A = i₄} {i₂} {f}         = ⊥-poly-elim f
    4PO-ax {A = i₂} {i₃} {f}         = ⊥-poly-elim f
    4PO-ax {A = i₄} {i₃} {f}         = ⊥-poly-elim f

    4AssocPf : ∀{l}{A B C D : 4Obj {l}} {f : el (4Hom A B)} {g : el (4Hom B C)}{h : el (4Hom C D)}
      → ⟨ 4Hom A D ⟩[ f ○[ 4Comp {l} {A}{B}{D} ] (g ○[ 4Comp {l}{B}{C}{D} ] h) ≡ (f ○[ 4Comp {l} {A}{B}{C} ] g) ○[ 4Comp {l}{A}{C}{D} ] h ]
    4AssocPf {l}{A}{B}{C}{D} {f}{g} = 4PO-ax {l} {A} {D}

    4IdPF : {l : Level}{A B : 4Obj {l}} {f : el (4Hom A B)}
      → ⟨ 4Hom A B ⟩[ (4Id {l}{A}) ○[ 4Comp {l}{A}{A}{B} ] f ≡ f ○[ 4Comp {l}{A}{B}{B} ] (4Id {l}{B}) ]
    4IdPF {l}{A}{B}{f} = 4PO-ax {l} {A} {B}

    -- We have a category.
    4cat : {l : Level} → Cat {l}
    4cat {l} = record { Obj = 4Obj {l};
                        Hom = 4Hom;
                       comp = λ {A} {B} {C} → 4Comp {l} {A}{B}{C};
                         id = λ {A} → 4Id {l}{A};
                    assocPf = λ {A} {B} {C} {D} {f} {g} {h} → 4AssocPf {l}{A}{B}{C}{D}{f}{g}{h};
                       idPfCom = λ {A} {B} {f} → 4IdPF {l}{A}{B}{f};
                       idPf = λ {A} {B} {f} → 4PO-ax {l} {A} {B}}

    -- We have a preorder.
    4po : {l : Level} → PO {l}
    4po {l} = record { ℙ = 4cat;
                    POax = λ {A} {B} {f} {g} → 4PO-ax {l} {A} {B} {f} {g} }


module Issue59 where

data Nat : Set where
  zero : Nat
  suc  : Nat → Nat

-- This no longer termination checks with the
-- new rules for with.
bad : Nat → Nat
bad n with n
... | zero  = zero
... | suc m = bad m

-- This shouldn't termination check.
bad₂ : Nat → Nat
bad₂ n with bad₂ n
... | m = m


open import Oscar.Prelude
open import Oscar.Class.Successor₀
open import Oscar.Class.Successor₁
open import Oscar.Class.Injectivity
open import Oscar.Class.Thickandthin
open import Oscar.Class.Congruity
open import Oscar.Class.Fmap
open import Oscar.Data.¶
open import Oscar.Data.Fin
open import Oscar.Data.Proposequality
open import Oscar.Data.Maybe
import Oscar.Property.Monad.Maybe
import Oscar.Class.Congruity.Proposequality

module Oscar.Property.Thickandthin.FinFinProposequalityMaybeProposequality where

instance

  𝓢uccessor₀¶ : 𝓢uccessor₀ ¶
  𝓢uccessor₀¶ .𝓢uccessor₀.successor₀ = ↑_

  [𝓢uccessor₁]Fin : [𝓢uccessor₁] Fin
  [𝓢uccessor₁]Fin = ∁

  𝓢uccessor₁Fin : 𝓢uccessor₁ Fin
  𝓢uccessor₁Fin .𝓢uccessor₁.successor₁ = ↑_

  [𝓘njectivity₁]Fin : ∀ {m} → [𝓘njectivity₁] (λ (_ : Fin m) → Fin (⇑₀ m)) Proposequality Proposequality
  [𝓘njectivity₁]Fin = ∁

  𝓘njectivity₁Fin : ∀ {m} → 𝓘njectivity₁ (λ (_ : Fin m) → Fin (⇑₀ m)) Proposequality Proposequality
  𝓘njectivity₁Fin .𝓘njectivity₁.injectivity₁ ∅ = ∅

  [𝓣hick]Fin,Fin : [𝓣hick] Fin Fin
  [𝓣hick]Fin,Fin = ∁

  𝓣hickFin,Fin : 𝓣hick Fin Fin
  𝓣hickFin,Fin .𝓣hick.thick {∅} () ∅
  𝓣hickFin,Fin .𝓣hick.thick {↑ _} _ ∅ = ∅
  𝓣hickFin,Fin .𝓣hick.thick ∅ (↑ y) = y
  𝓣hickFin,Fin .𝓣hick.thick (↑ x) (↑ y) = ↑ thick x y

  [𝓣hin]Fin,Fin : [𝓣hin] Fin Fin
  [𝓣hin]Fin,Fin = ∁

  𝓣hinFin,Fin : 𝓣hin Fin Fin
  𝓣hinFin,Fin .𝓣hin.thin ∅ = ↑_
  𝓣hinFin,Fin .𝓣hin.thin (↑ x) ∅ = ∅
  𝓣hinFin,Fin .𝓣hin.thin (↑ x) (↑ y) = ↑ (thin x y)

  [𝓘njectivity₂,₁]ThinFinFin : ∀ {m} → [𝓘njectivity₂,₁] (𝔱hin Fin Fin m) Proposequality Proposequality
  [𝓘njectivity₂,₁]ThinFinFin = ∁

  𝓘njectivity₂,₁ThinFinFin : ∀ {m} → 𝓘njectivity₂,₁ (𝔱hin Fin Fin m) Proposequality Proposequality
  𝓘njectivity₂,₁ThinFinFin .𝓘njectivity₂,₁.injectivity₂,₁ ∅ ∅ = ∅
  𝓘njectivity₂,₁ThinFinFin .𝓘njectivity₂,₁.injectivity₂,₁ (↑ _) {∅}    {∅} _ = ∅
  𝓘njectivity₂,₁ThinFinFin .𝓘njectivity₂,₁.injectivity₂,₁ (↑ _) {∅}    {↑ _} ()
  𝓘njectivity₂,₁ThinFinFin .𝓘njectivity₂,₁.injectivity₂,₁ (↑ _) {↑ _}  {∅}   ()
  𝓘njectivity₂,₁ThinFinFin .𝓘njectivity₂,₁.injectivity₂,₁ (↑ x) {↑ _}  {↑ _} = congruity ↑_ ∘ injectivity₂,₁ x ∘ injectivity₁[ Proposequality ]

  [𝓒heck]FinFinMaybe : [𝓒heck] Fin Fin Maybe
  [𝓒heck]FinFinMaybe = ∁

  𝓒heckFinFinMaybe : 𝓒heck Fin Fin Maybe
  𝓒heckFinFinMaybe .𝓒heck.check ∅ ∅ = ∅
  𝓒heckFinFinMaybe .𝓒heck.check ∅ (↑ y) = ↑ y
  𝓒heckFinFinMaybe .𝓒heck.check {∅} (↑ ()) _
  𝓒heckFinFinMaybe .𝓒heck.check {↑ _} (↑ x) ∅ = ↑ ∅
  𝓒heckFinFinMaybe .𝓒heck.check {↑ _} (↑ x) (↑ y) = fmap′ ¶⟨<_⟩.↑_ $ check x y

  [𝓣hick/thin=1]FinFin : [𝓣hick/thin=1] Fin Fin Proposequality
  [𝓣hick/thin=1]FinFin = ∁

  𝓣hick/thin=1FinFin : 𝓣hick/thin=1 Fin Fin Proposequality
  𝓣hick/thin=1FinFin .𝓣hick/thin=1.thick/thin=1 x ∅ = ∅
  𝓣hick/thin=1FinFin .𝓣hick/thin=1.thick/thin=1 ∅ (↑ y) = ∅
  𝓣hick/thin=1FinFin .𝓣hick/thin=1.thick/thin=1 (↑ x) (↑ y) = congruity ↑_ (thick/thin=1 x y)

  [𝓒heck/thin=1FinFin] : [𝓒heck/thin=1] Fin Fin Maybe Proposequality
  [𝓒heck/thin=1FinFin] = ∁

  𝓒heck/thin=1FinFin : 𝓒heck/thin=1 Fin Fin Maybe Proposequality
  𝓒heck/thin=1FinFin .𝓒heck/thin=1.check/thin=1 ∅ y = ∅
  𝓒heck/thin=1FinFin .𝓒heck/thin=1.check/thin=1 (↑ x) ∅ = ∅
  𝓒heck/thin=1FinFin .𝓒heck/thin=1.check/thin=1 (↑ x) (↑ y) rewrite check/thin=1 {_≈_ = Proposequality⟦ Maybe _ ⟧} x y = ∅

  IsThickandthinFinFin : IsThickandthin Fin Fin Proposequality Maybe Proposequality
  IsThickandthinFinFin = ∁

ThickandthinFinFin : Thickandthin _ _ _ _ _ _
ThickandthinFinFin = ∁ Fin Fin Proposequality Maybe Proposequality


{-# OPTIONS --safe #-}

open import Generics.Prelude hiding (lookup; pi; curry)
open import Generics.Telescope
open import Generics.Desc
open import Generics.All
open import Generics.HasDesc
import Generics.Helpers as Helpers

open import Relation.Binary.HeterogeneousEquality.Core using (_≅_; refl)

module Generics.Constructions.Cong
  {P I ℓ} {A : Indexed P I ℓ} (H : HasDesc {P} {I} {ℓ} A)
  {p}
  where

open HasDesc H

private
  variable
    V       : ExTele P
    i i₁ i₂ : ⟦ I ⟧tel p
    v v₁ v₂ : ⟦ V ⟧tel p

-----------------------
-- Type of congruences

levelCongCon : (C : ConDesc P V I) → Level
levelCongCon (var _) = ℓ
levelCongCon (π {ℓ} _ _ C) = ℓ ⊔ levelCongCon C
levelCongCon (A ⊗ B) = levelIndArg A ℓ ⊔ levelCongCon B

CongCon : (C   : ConDesc P V I)
          (mk₁ : ∀ {i₁} → ⟦ C ⟧Con A′ (p , v₁ , i₁) → ⟦ D ⟧Data A′ (p , i₁))
          (mk₂ : ∀ {i₂} → ⟦ C ⟧Con A′ (p , v₂ , i₂) → ⟦ D ⟧Data A′ (p , i₂))
        → Set (levelCongCon C)
-- If non-inductive arguments are equal
CongCon (π (n , ai) S C) mk₁ mk₂
  = {s₁ : < relevance ai > S _}
    {s₂ : < relevance ai > S _}
  → s₁ ≅ s₂
  → CongCon C (λ x → mk₁ (s₁ , x))
              (λ x → mk₂ (s₂ , x))
-- And inductive arguments  are equal
CongCon (A ⊗ B) mk₁ mk₂
  = {f₁ : ⟦ A ⟧IndArg A′ _}
    {f₂ : ⟦ A ⟧IndArg A′ _}
  → f₁ ≅ f₂
  → CongCon B (λ x → mk₁ (f₁ , x))
              (λ x → mk₂ (f₂ , x))
-- Then applying the constructor to both sets should lead
-- to equal values
CongCon (var f) mk₁ mk₂
  = constr (mk₁ refl) ≅ constr (mk₂ refl)

Cong : ∀ k → Set (levelCongCon (lookupCon D k))
Cong k = CongCon (lookupCon D k) (k ,_) (k ,_)

----------------------
-- Generic congruence

deriveCong : ∀ k → Cong k
deriveCong k = congCon (lookupCon D k)
  where
   congCon : (C : ConDesc P V I)
             {mk : ∀ {i} → ⟦ C ⟧Con A′ (p , v , i) → ⟦ D ⟧Data A′ (p , i)}
           → CongCon C mk mk
   congCon (var f) = refl
   congCon (π ai S C) refl = congCon C
   congCon (A ⊗ B) refl = congCon B


module _ where

open import Common.Prelude hiding (_>>=_)
open import Common.Reflection

infix -100 This:_ this:_
data This:_ {a} {A : Set a} : A → Set where
  this:_ : ∀ x → This: x

macro
  runT : Tactic → Tactic
  runT m = m

  evalT : ∀ {a} {A : Set a} → TC A → Tactic
  evalT m hole = m >>= quoteTC >>= unify hole

-- The context on the rhs of each of the two functions below is the same, a single String

Γ = vArg (def (quote String) []) ∷ []

context-Γ₀ : String → This: Γ
context-Γ₀ s = this: evalT getContext

module _ (S : String) where
  context-Γ₁ : This: Γ
  context-Γ₁ = this: evalT getContext

replicate : {A : Set} → Nat → A → List A
replicate zero x = []
replicate (suc n) x = x ∷ replicate n x

f-type : Term
f-type = def (quote String) []

f-pats : Nat → List (Arg Pattern)
f-pats n = replicate n (vArg (var "_"))

f-term : Nat → Term
f-term n = var n []

defineFresh : Nat → Nat → TC QName
defineFresh #pats #term =
  freshName "f" >>= λ f →
  define (vArg f) (funDef f-type (clause (f-pats #pats) (f-term #term) ∷ [])) >>= λ _ →
  returnTC f

freshFun : Nat → Nat → TC Bool
freshFun #pats #term =
  catchTC (defineFresh #pats #term >>= λ _ → returnTC true)
          (returnTC false)

-- Check that the pattern list must be of length 0
-- and the context features 1 available variable.

define-Γ₀-0-0 : String → This: true
define-Γ₀-0-0 s = this: evalT (freshFun 0 0)

define-Γ₀-1-0 : String → This: false
define-Γ₀-1-0 s = this: evalT (freshFun 1 0)

define-Γ₀-1-1 : String → This: false
define-Γ₀-1-1 s = this: evalT (freshFun 0 1)

module _ (S : String) where
  define-Γ₁-0-0 : This: true
  define-Γ₁-0-0 = this: evalT (freshFun 0 0)

  define-Γ₁-0-1 : This: false
  define-Γ₁-0-1 = this: evalT (freshFun 0 1)

  define-Γ₁-1-0 : This: false
  define-Γ₁-1-0 = this: evalT (freshFun 1 0)

f₀ : String → String
f₀ s = runT λ hole → defineFresh 0 0 >>= λ f → unify hole (def f [])

f₁ : String → String
f₁ = λ s → runT λ hole → defineFresh 0 0 >>= λ f → unify hole (def f [])

f₂ : String → String
f₂ s = runT λ hole → defineFresh 0 0 >>= λ f → unify hole (def f [])
  where x = 0


open import Prelude

module Implicits.Semantics.Preservation where

open import Implicits.Syntax
open import Implicits.WellTyped
open import Implicits.Semantics.Type
open import Implicits.Semantics.Context
open import Implicits.Semantics.Lemmas
open import Implicits.Semantics.RewriteContext
open import Implicits.Semantics.Term
open import SystemF.Everything as F using ()

module Preservation
  (_⊢ᵣ_ : ∀ {ν} → ICtx ν → Type ν → Set)
  (⟦_,_⟧r : ∀ {ν n} {K : Ktx ν n} {a} → (proj₂ K) ⊢ᵣ a → K# K →
            ∃ λ t → ⟦ proj₁ K ⟧ctx→ F.⊢ t ∈ ⟦ a ⟧tp→) where

  open TypingRules _⊢ᵣ_
  open TermSemantics _⊢ᵣ_ ⟦_,_⟧r

  -- interpretation of well-typed terms in System F preserves type
  ⟦⟧-preserves-tp : ∀ {ν n} {K : Ktx ν n} {t a} → (wt-t : K ⊢ t ∈ a) → (m : K# K) →
                    ⟦ proj₁ K ⟧ctx→ F.⊢ ⟦ wt-t , m ⟧ ∈ ⟦ a ⟧tp→
  ⟦⟧-preserves-tp {K = K} (var x) m = subst-wt-var (sym $ lookup-⟦⟧ctx→ (proj₁ K) x) (F.var x)
    where
      subst-wt-var = subst (λ a → ⟦ proj₁ K ⟧ctx→ F.⊢ (F.var x) ∈ a)
  ⟦⟧-preserves-tp {K = K} {a = ∀' a} (Λ wt-e) m with ⟦⟧-preserves-tp wt-e (#tvar m)
  ... | ih = F.Λ (subst-wt-ctx (⟦weaken⟧ctx→ (proj₁ K)) ih)
    where
      subst-wt-ctx = subst (λ c → c F.⊢ ⟦ wt-e , #tvar m ⟧ ∈ ⟦ a ⟧tp→)
  ⟦⟧-preserves-tp (λ' a wt-e) m with ⟦⟧-preserves-tp wt-e (#var a m)
  ⟦⟧-preserves-tp (λ' a wt-e) m | ih = F.λ' ⟦ a ⟧tp→ ih
  ⟦⟧-preserves-tp {K = K} (_[_] {a = a} wt-tc b) m with ⟦⟧-preserves-tp wt-tc m
  ... | ih = subst-tp (sym $ ⟦a/sub⟧tp→ a b) (ih F.[ ⟦ b ⟧tp→ ])
    where
      subst-tp = subst (λ c → ⟦ proj₁ K ⟧ctx→ F.⊢ ⟦ wt-tc [ b ] , m ⟧ ∈ c)
  ⟦⟧-preserves-tp (wt-f · wt-e) m with ⟦⟧-preserves-tp wt-f m | ⟦⟧-preserves-tp wt-e m
  ⟦⟧-preserves-tp (wt-f · wt-e) m | ih | y = ih F.· y
  ⟦⟧-preserves-tp (ρ {a = a} unamb-a wt-e) m with ⟦⟧-preserves-tp wt-e (#ivar a m)
  ⟦⟧-preserves-tp (ρ {a = a} unamb-a wt-e) m | ih = F.λ' ⟦ a ⟧tp→ ih
  ⟦⟧-preserves-tp (wt-r ⟨ e ⟩) m with ⟦⟧-preserves-tp wt-r m
  ⟦⟧-preserves-tp (wt-r ⟨ e ⟩) m | f-wt-r = f-wt-r F.· (proj₂ ⟦ e , m ⟧r)
  ⟦⟧-preserves-tp (wt-r with' wt-e ) m with ⟦⟧-preserves-tp wt-r m | ⟦⟧-preserves-tp wt-e m
  ⟦⟧-preserves-tp (wt-r with' wt-e ) m | f-wt-r | f-wt-e = f-wt-r F.· f-wt-e