Require Import List Arith Max Lia Wellfounded Bool.

From Undecidability.Shared.Libs.DLW.Utils
  Require Import list_focus utils_tac utils_list utils_nat.

Set Implicit Arguments.


Section list_choose_d.

  Variable (X : Type) (P Q : X Prop).

  Theorem list_choose_d l : ( x, In x l P x Q x)
                            ( x, In x l P x)
                            x, In x l Q x.
  Proof.
    induction l as [ | x l IHl ]; intros Hl.
    + right; intros _ [].
    + destruct (Hl x) as [ | ].
      * left; auto.
      * left; exists x; simpl; auto.
      * destruct IHl as [ (y & & ) | ].
        - intros; apply Hl; right; auto.
        - left; exists y; simpl; auto.
        - right; intros ? [ | ]; auto.
  Qed.


End list_choose_d.

Section bounded_choose_d.

  Variable (P Q : Prop).

  Theorem bounded_choose_d n : ( x, x < n P x Q x)
                            ( x, x < n P x)
                            x, x < n Q x.
  Proof.
    intros H.
    destruct list_choose_d with (P := P) (Q := Q) (l := list_an 0 n)
      as [ (x & & ) | ].
    + intro; rewrite list_an_spec; intro; apply H; .
    + left; exists x; split; auto.
      apply list_an_spec in ; .
    + right; intros x Hx; apply , list_an_spec; .
  Qed.


End bounded_choose_d.

Section bounded_min.

  Variable (P Q : Prop).

  Theorem bounded_min_d n : ( x, x < n P x Q x)
                            ( x, x < n P x y, y < x Q y)
                            x, x < n Q x.
  Proof.
    induction n as [ | n IHn ]; intros Hn.
    + right; intros; .
    + destruct IHn as [ (x & & & ) | ].
      * intros; apply Hn; .
      * left; exists x; msplit 2; auto; .
      * destruct (Hn n); auto.
        - left; exists n; msplit 2; auto.
        - right; intros x Hx.
          destruct (eq_nat_dec x n); subst; auto.
          apply ; .
  Qed.


End bounded_min.

Section list_choose_dep.

  Variable (X : Type) (P Q : X Prop).

  Theorem list_choose_dep l : ( x, In x l { P x } + { Q x })
                            { x | In x l P x }
                            + { x, In x l Q x }.
  Proof.
    induction l as [ | x l IHl ]; intros Hl.
    + right; intros _ [].
    + destruct (Hl x) as [ | ].
      * left; auto.
      * left; exists x; simpl; auto.
      * destruct IHl as [ (y & & ) | ].
        - intros; apply Hl; right; auto.
        - left; exists y; simpl; auto.
        - right; intros ? [ | ]; auto.
  Qed.


End list_choose_dep.

Section sinc_decidable.

  Variable (P : Prop)
           (f : )
           (Hf : n, f n < f (S n))
           (HP : n, P n k, n = f k).

  Let f_mono x y : x y f x f y.
  Proof.
    induction 1 as [ | y H IH ]; auto.
    apply le_trans with (1 := IH), lt_le_weak, Hf.
  Qed.


  Let f_smono x y : x < y f x < f y.
  Proof.
    intros H; apply f_mono in H.
    apply lt_le_trans with (2 := H), Hf.
  Qed.


  Let f_ge_n n : n f n.
  Proof.
    induction n as [ | n IHn ]; try .
    apply le_trans with (2 := Hf _); .
  Qed.


  Let unbounded n : k, n k P k.
  Proof. exists (f n); split; auto; rewrite HP; exists n; auto. Qed.

  Let decidable n : { P n } + { P n }.
  Proof.
    destruct (@bounded_search (S n) ( i f i = n))
      as [ (i & & ) | ].
    + intros i _; destruct (eq_nat_dec (f i) n); tauto.
    + left; rewrite HP; eauto.
    + right; rewrite HP; intros (k & Hk).
      symmetry in Hk; generalize Hk; apply .
      rewrite Hk; apply le_n_S; auto.
  Qed.


  Theorem sinc_decidable : ( n, k, n k P k)
                         * ( n, { P n } + { P n }).
  Proof. split; auto. Qed.

End sinc_decidable.

Section decidable_sinc.

  Variable (P : Prop)
           (Punb : n, k, n k P k)
           (Pdec : n, { P n } + { P n }).

  Let next n : { k | P k n k x, P x x < n k x }.
  Proof.
    destruct min_dec with (P := k P k n k)
      as (k & ( & ) & ).
    + intros i; destruct (Pdec i); destruct (le_lt_dec n i); try tauto; right; intro; .
    + destruct (Punb (S n)) as (k & & ).
      exists k; split; auto; .
    + exists k; repeat (split; auto).
      intros x Hx.
      destruct (le_lt_dec n x); try .
      right; apply ; auto.
  Qed.


  Let f := fix f n := match n with
    | 0 proj1_sig (next 0)
    | S n proj1_sig (next (S (f n)))
  end.

  Let f_sinc n : f n < f (S n).
  Proof.
    simpl.
    destruct (next (S (f n))) as (?&?&?&?); auto.
  Qed.


  Let f_select x : { n | f n x < f (S n) } + { x < f 0 }.
  Proof.
    induction x as [ | x IHx ].
    + destruct (eq_nat_dec 0 (f 0)) as [ H | H ].
      * left; exists 0; rewrite H at 2 3; split; auto.
      * right; .
    + destruct IHx as [ (n & Hn) | Hx ].
      * destruct (eq_nat_dec (S x) (f (S n))) as [ H | H ].
        - left; exists (S n); rewrite H; split; auto.
        - left; exists n; .
      * destruct (eq_nat_dec (S x) (f 0)) as [ H | H ].
        - left; exists 0; rewrite H; split; auto.
        - right; .
  Qed.


  Let f_P n : P n k, n = f k.
  Proof.
    split.
    + intros Hn.
      destruct (f_select n) as [ (k & Hk) | C ].
      * simpl in Hk.
        destruct (next (S (f k))) as (m & & & ); simpl in Hk.
        apply in Hn.
        destruct Hn as [ Hn | Hn ]; try .
        exists k; .
      * simpl in C.
        destruct (next 0) as (m & & & ); simpl in C.
        apply in Hn; .
    + intros (k & Hk); subst.
      induction k as [ | k IHk ]; simpl.
      * destruct (next 0) as (m & & & ); simpl; auto.
      * destruct (next (S (f k))) as (m & & & ); simpl; auto.
  Qed.


  Theorem decidable_sinc : { f | ( n, f n < f (S n))
                               ( n, P n k, n = f k) }.
  Proof. exists f; auto. Qed.

End decidable_sinc.