\import Algebra.Meta
\import Data.Or
\import Function.Meta
\import Logic.Unique
\import Logic.Meta
\import Meta
\import Paths
\import Paths.Meta

\data Empty

\func absurd {A : \Type} (x : Empty) : A

\func Not (A : \Type) => A -> Empty

\func \infix 1 /= {A : \Type} (a a' : A) => Not (a = a')

\lemma /=-sym {A : \Type} {a a' : A} (p : a /= a') : a' /= a
  => \lam r => p (inv r)

\data TruncP (A : \Type)
  | inP A
  | truncP (a a' : TruncP A) : a = a'
  \where {
    \use \level levelProp {A : \Type} (a a' : TruncP A) : a = a' => path (truncP a a')

    \lemma remove {A : \Type} (p : isProp A) (t : TruncP A) : A \level p \elim t
      | inP a => a

    \lemma remove' {A : \Prop} (t : TruncP A) : A \elim t
      | inP a => a

    \lemma rec {A B : \Type} (p : isProp B) (t : TruncP A) (f : A -> B) : B \level p \elim t
      | inP a => f a

    \lemma rec-eval {A B : \Type} {p : isProp B} {a : A} {f : A -> B} : rec p (inP a) f = f a \level isProp.=>isSet p _ _
      => p _ _

    \lemma rec-set {A : \Type} {B : \Set} (t : TruncP A) (f : A -> B) (p : \Pi (a a' : A) -> f a = f a')
      : \Sigma (b : B) ( (a : A) (f a = b)) \level level p \elim t
      | inP a => (f a, inP (a, idp))
      \where {
        \lemma level (p : \Pi (a a' : A) -> f a = f a') : isProp (\Sigma (b : B) ( (a : A) (f a = b)))
          => \lam (b, inP (a,fa=b)) (b', inP (a',fa'=b')) => ext $ inv fa=b *> p a a' *> fa'=b'
      }

    \func map {A B : \Type} (t : TruncP A) (f : A -> B) : TruncP B \elim t
      | inP a => inP (f a)
  }

\lemma prop-pi {A : \Prop} {a a' : A} : a = a'
  => \case truncP (inP a) (inP a') __ \with {
    | inP x => x
  }

\lemma prop-isProp {A : \Prop} : isProp A
  => \lam _ _ => prop-pi

\lemma set-pi {A : \Set} {a b : A} {p q : a = b} : p = q
  => prop-pi

\lemma prop-dpi (A : I -> \Prop) (a : A left) (a' : A right) : Path A a a'
  => pathOver (prop-isProp (coe A a right) a')

\data ToProp (A : \Type) (p : isProp A)
  | toProp A
  \where {
    \lemma fromProp {A : \Type} {p : isProp A} (t : ToProp A p) : A \level p \elim t
      | toProp a => a

    \use \level levelProp {A : \Type} {p : isProp A} (x y : ToProp A p) : x = y \elim x, y
      | toProp a, toProp a' => pmap toProp (p a a')
  }

\truncated \data \infixr 2 || (A B : \Type) : \Prop
  | byLeft A
  | byRight B
  \where {
    \lemma rec {A B C : \Type} (p : isProp C) (f : A -> C) (g : B -> C) (t : A || B) : C \level p \elim t
      | byLeft a => f a
      | byRight b => g b

    \lemma rec' {A B : \Type} {C : \Prop} (f : A -> C) (g : B -> C) (t : A || B) : C \elim t
      | byLeft a => f a
      | byRight b => g b

    \func map {A B C D : \Type} (f : A -> C) (g : B -> D) (t : A || B) : C || D \elim t
      | byLeft a => byLeft (f a)
      | byRight b => byRight (g b)

    \lemma fromOr {A B : \Type} (p : Or A B) : A || B \elim p
      | inl a => byLeft a
      | inr b => byRight b

    \lemma toOr {A B : \Type} (p : A || B) : TruncP (Or A B) \elim p
      | byLeft a => inP (inl a)
      | byRight b => inP (inr b)

    \lemma flip {A B : \Type} (e : A || B) : B || A \elim e
      | byLeft a => byRight a
      | byRight b => byLeft b
  }

\func \infix 0 <-> (P Q : \Prop) => \Sigma (P -> Q) (Q -> P)

\lemma <->_= {P Q : \Prop} : (P <-> Q) <-> (P = Q)
  => (\lam p => ext p, \lam p => rewrite p (\lam x => x, \lam x => x))

\lemma <->refl {P : \Prop} : P <-> P
  => (\lam p => p, \lam p => p)

\lemma <->trans {P Q S : \Prop} (f : P <-> Q) (g : Q <-> S) : P <-> S
  => (\lam p => g.1 (f.1 p), \lam s => f.2 (g.2 s))

\lemma <->sym {P Q : \Prop} (f : P <-> Q) : Q <-> P
  => (f.2,f.1)

\func OneOf (l : Array \Type) =>  (P : l) P

\func ElemOf (l : Array \Type) => Given (P : l) P

\type arraySubset {X : \Type} (l : Array X) (x : X) =>  (y : l) (y = x)

\lemma propExt {A B : \Prop} (f : A -> B) (g : B -> A) : A = B =>
  path (iso f g (\lam _ => prop-pi) (\lam _ => prop-pi))
  \where {
    \lemma dir {A B : \Prop} (p : A = B) (a : A) : B => transport (\lam X => X) p a

    \lemma conv {A B : \Prop} (p : A = B) (b : B) : A => dir (inv p) b
  }

\meta isProp-prover => \lam x y => cases (x,y) (cong prop-pi <|> contradiction)

\class NegatedProp (P : \Prop)
  | isNegated : Not (Not P) -> P

\instance EmptyNegated : NegatedProp Empty
  | isNegated p => p \lam x => x