{-# OPTIONS --safe #-}

module Finite where

open import Prelude
open import Function.Properties
open import Data.List.Membership

 : Type a  Type a
 A =  n ×  Fin n  A 


open import Data.Nat.Properties using (isSetNat)
open import Data.Fin.Injective
open import Isomorphism.Properties

isPropℰ : isProp ( A)
isPropℰ (n , xe) (m , ye) = Σ≡Prop  _  squash) (∥rec2∥ (isSetNat _ _)  lhs rhs  Fin-inj (ua (lhs  ≃-trans  ≃-sym rhs))) xe ye)

trav :  {n} {P : Lift {ℓ₂ = b} (Fin n)  Type p}  (∀ x   P x )   (∀ x  P x) 
trav {n = zero } f =   ()) 
trav {b = b} {n = suc n} f =
  let h = f (lift zero)
  in ∥liftA2∥  { x xs (lift zero)  x ; x xs (lift (suc i))  xs (lift i) }) h (trav {b = b} (f ∘′ lift ∘′ suc ∘′ lower))

travℰ :  A  ((x : A)   P x )   ((x : A)  P x) 
travℰ (n , p) k = ∥rec∥ squash  e  let h = ua (≃-trans (isoToEquiv ⇔-lift) e) in subst  t   {P : t  Type _}  ((x : t)   P x )   (∀ x  P x)  ) h trav k) p

module _ where
  open import Finite.OnLists

  ≃⇒ℰ! :  n × (Fin n  A)  ℰ! A
  ≃⇒ℰ! (n , p) .fst = tabulate (p .fst)
  ≃⇒ℰ! (n , p) .snd x =
    let e = p .snd .equiv-proof x
        h = tabulate-∈ (p .fst) (e .fst .fst)
    in subst (_∈ tabulate (p .fst)) (e .fst .snd) h

  module _ {A : Type a} where
    ℰΣ :  A  ((x : A)   (P x))   (Σ A P)
    ℰΣ (n , xs) k = ∥rec2∥ isPropℰ conv xs (travℰ (n , xs) (snd ∘′ k))
      where
      conv : Fin n  A  ((x : A)  Fin (k x .fst)  P x)   (Σ A P)
      conv e k′ =
        let h = ≃⇒ℰ! (_ , e) |Σ| λ x  ≃⇒ℰ! (_  , k′ x) 
        in map₂ ∣_∣ (ℬ→ℱ (ℰ!⇒ℬ h))

open import Data.Nat.Order

⇔→ℰ : A  B   A   B
⇔→ℰ i = map₂ (∥map∥ (flip ≃-trans (isoToEquiv i)))


Equiv⇒SplitSurj : (f : A  B)  isEquiv f  SplitSurjective f
Equiv⇒SplitSurj f x y = x .equiv-proof y .fst

≃⇒↠! : A  B  A ↠! B
≃⇒↠! = map₂ (Equiv⇒SplitSurj _)

ℰ⇒Discrete :  A  Discrete A
ℰ⇒Discrete = ∥rec∥ isPropDiscrete (flip Discrete-↠! discreteFin  ≃⇒↠!) ∘′ snd


open import Data.Fin.Properties using (isPropFin1; Fin⊥)

ℰ⇒Dec :  A  Dec  A 
ℰ⇒Dec (zero  , xs) = no (∥rec2∥  ())  e x  Fin⊥ .fun (e .snd .equiv-proof x .fst .fst))  xs)
ℰ⇒Dec (suc n , xs) = yes (∥map∥ ((_$ zero)  fst) xs)

open import Cubical.Foundations.Prelude using (isContr→isProp)

isContr⇒ℰ : isContr A   A
isContr⇒ℰ (x , p) .fst = 1
isContr⇒ℰ (x , p) .snd =
              where .fst _  x
                       .snd .equiv-proof y .fst .fst  0
                       .snd .equiv-proof y .fst .snd  p y
                       .snd .equiv-proof y .snd (zero , q) i  zero , isProp→isSet (isContr→isProp (x , p)) x y (p y) q i) 

¬⇒ℰ : ¬ A   A
¬⇒ℰ ¬A .fst = 0
¬⇒ℰ ¬A .snd =
   where .fst ()
            .snd .equiv-proof y  absurd (¬A y) ) 

ℰ× :  A   B   (A × B)
ℰ× x y = ℰΣ x (const y)

ℰ⟨⊤⟩ :  
ℰ⟨⊤⟩ = isContr⇒ℰ (tt , λ _  refl)

open import Data.Nat.Properties

private variable n m : 

ℰ⟨Fin⟩ :  (Fin n)
ℰ⟨Fin⟩ {n = n} = n ,  isoToEquiv  id-⇔ 

ℰ⟨ℕ≤⟩ :  ( m × m  n)
ℰ⟨ℕ≤⟩ = suc _ ,  isoToEquiv lemma 
  where
  fin→≤ : Fin (suc n)   m × m  n 
  fin→≤ zero    = zero , tt
  fin→≤ {n = suc n} (suc f) = let g , p = fin→≤ {n = n} f in suc g , p

  ≤→fin :  m × m  n  Fin (suc n)
  ≤→fin (zero , p) = zero
  ≤→fin {n = suc n} (suc m , p) = suc (≤→fin {n} (m , p))

  linv : (x : Fin (suc n))  ≤→fin (fin→≤ x)  x
  linv zero = refl
  linv {n = suc n} (suc x) = cong suc (linv {n} x)

  rinv : (x :  m × m  n)  fin→≤ (≤→fin x)  x
  rinv {n} (zero , p) = refl
  rinv {suc n} (suc m , p) = cong (map-Σ suc id) (rinv {n} (m , p))

  lemma : Fin (suc n)  ( m × m  n)
  lemma .fun = fin→≤
  lemma .inv = ≤→fin
  lemma .rightInv = rinv
  lemma .leftInv = linv

DecProp⇒ℰ : isProp A  Dec A   A
DecProp⇒ℰ isPropA (yes p) = isContr⇒ℰ (p , isPropA p)
DecProp⇒ℰ isPropA (no ¬p) = ¬⇒ℰ ¬p

Discrete⇒ℰ : Discrete A  {x y : A}   (x  y)
Discrete⇒ℰ d = DecProp⇒ℰ (Discrete→isSet d _ _) (d _ _)

ℰ? : (∀ x  isProp (P x))   A  ((x : A)  Dec (P x))   (Σ A P)
ℰ? pIsProp ℰA ℰP? = ℰΣ ℰA λ x  DecProp⇒ℰ (pIsProp x) (ℰP? x)


module _ (ℰ⟨A⟩ :  A) where
  ℰ⟨List·length⟩ :  n   (Σ[ xs  List A ] × length xs  n)
  ℰ⟨List·length⟩ zero    = ⇔→ℰ lemma ℰ⟨⊤⟩
    where
    lemma :   (Σ[ xs  List A ] × length xs  0)
    lemma .fun _ = [] , refl
    lemma .inv _ = tt
    lemma .rightInv ([] , p) = Σ≡Prop  _  isSetNat _ _) refl
    lemma .rightInv (x  xs , p) = absurd (suc≢zero p)
    lemma .leftInv  _ = refl
  ℰ⟨List·length⟩ (suc n) = ⇔→ℰ lemma (ℰ× ℰ⟨A⟩ (ℰ⟨List·length⟩ n))
    where
    lemma : (A × Σ[ xs  List A ] × length xs  n)  (Σ[ xs  List A ] × length xs  suc n)
    lemma .fun (x , xs , p) = x  xs , cong suc p
    lemma .inv ([] , p) = absurd (zero≢suc p)
    lemma .inv (x  xs , p) = x , xs , suc-inj p
    lemma .rightInv ([] , p) = absurd (zero≢suc p)
    lemma .rightInv (x  xs , p) = Σ≡Prop  _  isSetNat _ _) refl
    lemma .leftInv (x , xs , p) = refl

  open import Isomorphism.Properties

  ℰ⟨List≤length⟩ :  n   (Σ[ xs  List A ] × length xs  n)
  ℰ⟨List≤length⟩ n = ⇔→ℰ lemma (ℰΣ (ℰ⟨ℕ≤⟩ {n}) (ℰ⟨List·length⟩ ∘′ fst))
    where
    lemma : (Σ[ p  ( m × m  n) ] × Σ[ xs  List A ] × length xs  fst p)
           (Σ[ xs  List A ] × length xs  n)
    lemma .fun ((m , p) , xs , q) = xs , subst (_≤ _) (sym q) p
    lemma .inv (xs , q) = (length xs , q) , xs , refl
    lemma .rightInv (xs , q) = Σ≡Prop  x  isProp≤ {length x}  ) refl
    lemma .leftInv  ((m , p) , xs , q) = ΣPathP (Σ≡Prop  x  isProp≤ {x}) q , toPathP (Σ≡Prop  x  isSetNat _ _) (transportRefl xs)))

  ℰ×NoDup⇒List≤ : (xs : List A)  NoDup xs  length xs  ℰ⟨A⟩ .fst
  ℰ×NoDup⇒List≤ xs nd =
    let h = ((xs !!_) , NoDup⇒Inj xs nd)  (Fin (length xs)  A)
    in ∥rec∥ (isProp≤ {length xs})  p  ↣⇒≤ (↣-trans h (_ , ⇔-fun-inj (sym-⇔ (equivToIso p)) _ _))) (ℰ⟨A⟩ .snd)

  ℰ⟨List⟩  :  (Σ[ xs  List A ] × NoDup xs)
  ℰ⟨List⟩ = ⇔→ℰ (Σ-assoc  trans-⇔   cong-Σ lemma) (ℰ?  _  isPropNoDup _) (ℰ⟨List≤length⟩ (ℰ⟨A⟩ .fst)) (NoDups? (ℰ⇒Discrete ℰ⟨A⟩) ∘′ fst))
    where
    lemma : (xs : List A)  ((length xs  ℰ⟨A⟩ .fst) × NoDup xs)  NoDup xs
    lemma xs .fun = snd
    lemma xs .inv nd = ℰ×NoDup⇒List≤ xs nd , nd
    lemma xs .rightInv _ = refl
    lemma xs .leftInv x = ΣPathP (isProp≤ {length xs} _ _  , refl)

module _ {P : A  Type p} where
  ℰ⇒search :  A  (P? :  (x : A)  Dec (P x))  Dec   x × P x 
  ℰ⇒search ℰA P? = ∥rec∥ (isPropDec squash) (dec-trunc  search) (ℰA .snd)
    where
    search′ : (f : Fin n  A)  Dec ( i × P (f i))
    search′ {n = zero}  f = no λ ()
    search′ {n = suc n} f with P? (f 0) | search′ (f  suc)
    ... | yes p | _ = yes (0 , p)
    ... | no ¬p | yes (i , p) = yes (suc i , p)
    ... | no ¬p | no ¬ps = no λ { (zero , p)  ¬p p ; (suc i , p)  ¬ps (i , p) }

    open import Cubical.Foundations.Everything using (equivToIso)

    search : (Fin n  A)  Dec ( x × P x)
    search f≃A = let f⇔A = equivToIso f≃A in
      iff-dec
        (map-Σ (f⇔A .fun) id ,
         map-Σ (f⇔A .inv) (subst P (sym (f⇔A .rightInv _)))) (search′ (f⇔A .fun))