\import Arith.Nat
\import Data.Array
\import Data.Or
\import Function
\import Function.Meta ($)
\import Logic
\import Logic.Meta
\import Meta
\import Paths
\import Relation.Equivalence
\import Set
\import Set.Fin
\import Set.Fin.Pigeonhole

\class KFinSet \extends BoundedPigeonholeSet {
  | finSurj :  (f : Fin finCard -> E) (isSurj f)
  | boundedPigeonhole => \case finSurj \with {
    | inP (f,s) => boundedPigeonhole {pigeonhole-surj (\new BoundedPigeonholeSet (Fin finCard) finCard (pigeonhole-fin __ id<suc)) f s}
  }

  \lemma search (A : E -> \Prop) (d : \Pi (e : E) -> Dec (A e)) : Dec ( (e : E) (A e))
    => \case finSurj \with {
      | inP (f,fs) => \case FinSet.searchFin (\lam j => A (f j)) (\lam j => d (f j)) \with {
        | inl a => yes (inP (f a.1, a.2))
        | inr q => no \lam (inP t) => \case fs t.1 \with {
          | inP (j,p) => q j (transportInv A p t.2)
        }
      }
    }

  \lemma search_yes_reduce {A : E -> \Prop} (d : \Pi (e : E) -> Dec (A e)) (e :  (e : E) (A e)) : search A d = yes e
    => prop-pi

  \lemma search_no_reduce {A : E -> \Prop} (d : \Pi (e : E) -> Dec (A e)) (q : Not ( (e : E) (A e))) : search A d = no q
    => prop-pi

  \lemma dec : Dec (TruncP E)
    => cases (finCard,finSurj) \with {
      | 0, inP (f,fs) => no \lam (inP x) => \case fs x \with {
        | inP ((),_)
      }
      | suc n, inP (f,fs) => yes $ inP (f 0)
    }
} \where {
    \lemma fromArray {A : \Set} (l : Array A) (p : \Pi (a : A) ->  (i : Fin l.len) (l i = a)) : KFinSet A l.len \cowith
      | finSurj => inP (l,p)

    \lemma toArray (A : KFinSet) :  (l : Array A)  a  (i : Fin l.len) (l i = a)
      => TruncP.map A.finSurj \lam (f,p) => (f,p)

    \lemma KFin+Dec=>Fin {A : \Set} (k : KFinSet A) {d : DecSet A} : FinSet A
      => \case toArray k \with {
        | inP (l,q) =>
          \let l' => nub l
          \in FinSet.fromArray l' (\lam a => TruncP.map (q a) \lam p =>
              \let t => nub-isSurj l p.1
              \in (t.1, t.2 *> p.2)) nub-isInj
      }
}

\lemma QuotientKFin {A : KFinSet} {R : A -> A -> \Type} : KFinSet (Quotient R) A.finCard \cowith
  | finSurj => TruncP.map A.finSurj \lam s => (\lam j => in~ (s.1 j), \lam (in~ x) => TruncP.map (s.2 x) \lam r => (r.1, pmap in~ r.2))