From Undecidability.Synthetic Require Import Definitions.
From Undecidability.Shared Require Import embed_nat Dec.

Require Import Vector.
Import VectorNotations.

Require Import ConstructiveEpsilon.

From Equations Require Import Equations.

Local Set Implicit Arguments.
Local Unset Strict Implicit.

Ltac first s := only 1: s.
Ltac last s := cycle -1; only 1: (s + fail).

Lemma vec_0_nil X (v : Vector.t X 0) : v = [].
Proof.
  now depelim v.
Qed.

Lemma vec_1_inv X (v : Vector.t X 1) : {a & v = [a]}.
Proof.
  repeat depelim v. eauto.
Qed.


Lemma vec_2_inv X (v : Vector.t X 2) : { a & { b & v = [a; b]} }.
Proof.
  repeat depelim v. eauto.
Qed.


Lemma vec_singleton {X} (a b : X) : Vector.In a [b] a = b.
Proof.
  inversion 1; now inversion .
Qed.


Synthetic computability


Definition core_valid {Y : Type} (core : option Y) :=
   y1 y2 k1 k2, core = Some core = Some = .

Record part (Y : Type) : Type := mkPart {
  core : option Y;
  valid : core_valid core
}.
Arguments mkPart {_} _ _.
Arguments valid [_] _.
Definition part_eval {Y : Type} (p : part Y) (y : Y) :=
   k, (core p) k = Some y.
Notation "p ▷ y" := (part_eval p y) (at level 30).
Notation "'partial' f " := ({| core := f; valid := _ |}) (at level 30, only printing).
Notation "A -\ B" := (A part B) (at level 30).

Definition (p : Prop) :
  ( x, dec (p x)) ex p sig p.
Proof.
  apply constructive_indefinite_ground_description_nat_Acc.
Qed.


Lemma totalise_part X (p : part X) : ( y, p y) {y & p y}.
Proof.
  intros H.
  assert ( k, y, p.(core) k = Some y) as H'% by firstorder.
  - destruct H' as [k H'']. destruct (p.(core) k) as [x|] eqn:Heq.
    + firstorder.
    + exfalso. now destruct H''.
  - intros k. destruct (p.(core) k) as [x|] eqn:H''.
    + left. eauto.
    + right. now intros [y Hy].
Qed.


Theorem totalise X Y (f : X -\ Y) : ( x, y, f x y) x, {y & f x y}.
Proof.
  intros H x. apply totalise_part, H.
Qed.

Definition partialise X Y (f : X Y) : X -\ Y.
Proof.
  intros x. exists ( _ Some (f x)). congruence.
Defined.


Lemma part_functional {X : Type} (p : part X) (x y : X) : p x p y x = y.
Proof.
  intros [ ] [ ].
  eapply (p.(valid)); eassumption.
Qed.


Program Definition comp_part_total Y Z (f : Y Z) (g : part Y) : part Z.
Proof.
  unshelve eexists.
  { intros k. exact (match g.(core) k with Some y Some (f y) | None None end). }
  intros .
  destruct (core _ ) as [|] eqn:, (core _ ) as [|] eqn:; try congruence.
  enough ( = ) by congruence.
  eapply part_functional; eexists; eassumption.
Defined.

Program Definition comp_part_partial Y Z (f : Y -\ Z) (g : part Y) : part Z.
Proof.
  unshelve eexists.
  { intros k. exact (match g.(core) k with Some y (f y).(core) k | None None end). }
  intros .
  destruct (core _ ) as [|] eqn:, (core _ ) as [|] eqn:; try congruence.
  assert ( = ) as .
  { eapply part_functional with (p := g); eexists; eassumption. }
  eapply part_functional; eexists; eassumption.
Defined.


Lemma enumerable_separable (X : Type) (P1 P2 : X Prop) :
  ( x, x x False)
  semi_decidable semi_decidable f : X -\ bool,
  ( x, x f x true)
  ( x, x f x false).
Proof.
  intros Pdisjoint [ ] [ ].
  unshelve eexists.
  { intros x. unshelve eexists.
    { intros k. refine (if x k then Some true else if x k then Some false else None). }
    intros .
    destruct ( x ) eqn:, ( x ) eqn:, ( x ) eqn:, ( x ) eqn:; try congruence.
    all: destruct (Pdisjoint x); apply +apply ; eauto. }
 split; intros x; split.
 - intros [k Hk]%. exists k. cbn. now rewrite Hk.
 - intros [k Hk]. apply . exists k. cbn in Hk. now destruct ( x k), ( x k).
 - intros [k Hk]%. exists k. cbn. destruct ( x k) eqn:Hk'.
   + destruct (Pdisjoint x); apply + apply ; eauto.
   + now rewrite Hk.
 - intros [k Hk]. apply . exists k. cbn in Hk. now destruct ( x k), ( x k).
Qed.



Theorem general_post (X : Type) (P Q : X Prop) s1 s2 :
  semi_decider P semi_decider Q ( x, P x Q x False)
   { f : X -\ bool | x, (P x f x true) (Q x f x false) }.
Proof.
  intros HD. unshelve eexists. 2: split; split.
  - intros x. exists ( n if x n then Some true else if x n then Some false else None).
    intros b b' n n'. destruct ( x n) eqn:, ( x n) eqn:.
    all: destruct ( x n') eqn:, ( x n') eqn:; try congruence.
    all: exfalso; apply (HD x); [ apply | apply ]; eauto.
  - intros [n Hn] % . exists n. cbn. now rewrite Hn.
  - intros [n Hn]. cbn in Hn. apply . exists n. destruct ( x n), ( x n); congruence.
  - intros [n Hn] % . exists n. cbn. rewrite Hn.
    enough ( x n = false) as by reflexivity.
    destruct ( x n) eqn:H; try reflexivity.
    exfalso. apply (HD x); [ apply | apply ]; eauto.
  - intros [n Hn]. cbn in Hn. apply . exists n. destruct ( x n), ( x n); congruence.
Qed.


Theorem general_post_dec (X : Type) (P Q : X Prop) s1 s2 :
  semi_decider P semi_decider Q ( x, P x Q x False)
   ( x, P x Q x) decidable P.
Proof.
  intros HD HE. destruct (general_post HD) as [f Hf]. unshelve eexists.
  - intros x. unshelve edestruct (@totalise _ _ f) as [b Hb]; try exact x.
    + intros y. destruct (HE y) as [H|H].
      * exists true. now apply Hf.
      * exists false. now apply Hf.
    + exact b.
  - cbn. intros x. destruct totalise as [b Hb]. unfold reflects.
    destruct b; firstorder congruence.
Qed.