Require Import List Arith Max Lia Wellfounded Bool Eqdep_dec.

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

Set Implicit Arguments.

Section le_lt_pirr.



  Scheme le_indd := Induction for le Sort Prop.

  Theorem le_pirr x y (H1 H2 : x y) : = .
  Proof.
    revert .
    induction as [ | m IH ] using le_indd; intro .

    change (le_n x) with (eq_rect _ ( n' x n') (le_n x) _ eq_refl).
    generalize (eq_refl x).
    pattern x at 2 4 6 10, .
    case ; [intro E | intros m l E].
    rewrite UIP_dec with ( := E) ( := eq_refl); auto.
    apply eq_nat_dec.
    contradiction (le_Sn_n m); subst; auto.

    change (le_S x m ) with (eq_rect _ ( n' x n') (le_S x m ) _ eq_refl).
    generalize (eq_refl (S m)).
    pattern (S m) at 1 3 4 6, .
    case ; [intro E | intros p E].
    contradiction (le_Sn_n m); subst; auto.
    injection E; intro; subst.
    rewrite (IH ).
    rewrite UIP_dec with ( := E) ( := eq_refl); auto.
    apply eq_nat_dec.
  Qed.


  Fact lt_pirr x y (H1 H2 : x < y) : = .
  Proof. simpl; intros; apply le_pirr. Qed.

End le_lt_pirr.

Section fin_reif.

  Variable (X : Type) (R : X Prop).

  Fact fin_reif n : ( i, i < n x, R i x)
                  s, i (Hi : i < n), R i (s i Hi).
  Proof.
    revert R; induction n as [ | n IHn ]; intros R HR.
    + assert (s : x, x < 0 X) by (intros; ).
      exists s; intros; .
    + destruct (HR 0) as (x & Hx); try .
      destruct IHn with (R := i x R (S i) x) as (s & Hs).
      { intros; apply HR; . }
      exists ( i match i with 0 _ x | S i Hi s i (lt_S_n i n Hi) end).
      intros [ | i ] Hi; simpl; auto.
  Qed.


End fin_reif.

Fact fin_reif_nat (R : Prop) n :
         ( i, i < n ex (R i)) s, i, i < n R i (s i).
Proof.
  intros HR.
  apply fin_reif in HR.
  destruct HR as (s & Hs).
  exists ( i match le_lt_dec n i with left _ 0 | right H s _ H end).
  intros i Hi; destruct (le_lt_dec n i); auto; .
Qed.


Section bounded_search.

  Theorem bounded_search m (P : Type) :
        ( n, n < m P n + (P n False))
      { n : & (n < m) * P n }%type + { n, n < m P n False }.
  Proof.
    revert P; induction m as [ | m IHm ]; intros P HP.
    + right; intros; .
    + destruct (HP 0) as [ | ]; try .
      * left; exists 0; split; auto; .
      * destruct IHm with (P := n P (S n)) as [ (n & & ) | ].
        - intros; apply HP; .
        - left; exists (S n); split; auto; .
        - right; intros [ | n ] Hn; auto.
          apply ; .
  Qed.


  Lemma bounded_min (P : Prop) :
        ( x, P x P x)
      n, ( k, k < n P k i, i < k P i) k, k < n P k.
  Proof.
    intros HP.
    induction n as [ | n IHn ].
    + right; intros; .
    + destruct IHn as [ (k & & & ) | H ].
      * left; exists k; repeat split; auto; .
      * destruct (HP n).
        - left; exists n; repeat split; auto.
        - right; intros k Hk.
          destruct (eq_nat_dec k n); subst; auto.
          apply H; .
  Qed.


  Lemma minimize (P : Prop) : ( x, P x P x) ( n, P n) n, P n i, i < n P i.
  Proof.
    intros HP (n & Hn).
    destruct (@bounded_min _ HP (S n)) as [ (k & & & ) | H ].
    + exists k; split; auto.
    + exfalso; apply H with n; auto.
  Qed.


  Lemma first_non_zero (f : ) n : f 0 = 0 f n 0 i, i < n ( k, k i f k = 0) f (i+1) 0.
  Proof.
    intros .
    destruct (@minimize ( i f i 0)) as (i & & ).
    + intro; destruct (eq_nat_dec (f x) 0); .
    + exists n; auto.
    + assert (i 0) as Hi by (intro; subst; destruct ; auto).
      exists (i-1); split; [ | split ].
      * destruct (le_lt_dec i n) as [ | ]; try .
        apply in ; destruct ; auto.
      * intros k Hk; generalize ( k); intros; .
      * replace (i-1+1) with i by ; auto.
  Qed.


End bounded_search.

Fact interval_dec a b i : { a i < b } + { i < a b i }.
Proof.
  destruct (le_lt_dec b i).
  + right; .
  + destruct (le_lt_dec a i).
    * left; .
    * right; .
Qed.



Definition lsum := fold_right plus 0.
Definition lmax := fold_right max 0.

Fact lmax_spec l x : lmax l x Forall ( y y x) l.
Proof.
  revert x; induction l as [ | y l IHl ]; simpl.
  + split; auto; try .
  + intros x; rewrite Forall_cons_inv, IHl, Nat.max_lub_iff; tauto.
Qed.


Fact lsum_app l r : lsum (lr) = lsum l+lsum r.
Proof.
  induction l as [ | x l IHl ]; simpl; auto; rewrite IHl; .
Qed.


Fact lsum_le x l : In x l x lsum l.
Proof.
  intros H; apply in_split in H.
  destruct H as (u & v & ).
  rewrite lsum_app; simpl; .
Qed.


Fact lmax_prop l x : In x l x lmax l.
Proof.
  specialize (proj1 (lmax_spec l _) (le_refl _)).
  rewrite Forall_forall; auto.
Qed.


Section new.

  Definition nat_new l := S (lmax l).

  Fact nat_new_spec l : In (nat_new l) l.
  Proof.
    assert ( x, In x l x < nat_new l) as H.
      induction l as [ | x l IHl ].
      intros _ [].
      intros y [ [] | Hy ]; apply le_n_S;
        [ apply le_max_l | ].
      apply IHl, le_S_n in Hy.
      apply le_trans with (1 := Hy), le_max_r.
    intros C; apply H in C; .
  Qed.


End new.

Local Notation Zero := false.
Local Notation One := true.

Fixpoint n : * bool :=
  match n with
    | 0 (0,Zero)
    | 1 (0,One)
    | S (S n) let (p,b) := n in (S p,b)
  end.

Fact div2_spec n : match n with
                     | (p,One) n = 2*p+1
                     | (p,Zero) n = 2*p
                   end.
Proof.
  induction n as [ [ | [ | n ] ] IHn ] using (well_founded_induction lt_wf); simpl; auto.
  specialize (IHn n).
  destruct ( n) as (p,[]); simpl in * |- *; .
Qed.


Fixpoint div2_2p1 p : (2*p+1) = (p,One).
Proof.
  destruct p as [ | p ].
  simpl; auto.
  replace (2*S p+1) with (S (S (2*p+1))) by .
  unfold ; fold ; rewrite div2_2p1; auto.
Qed.


Fixpoint div2_2p0 p : (2*p) = (p,Zero).
Proof.
  destruct p as [ | p ].
  simpl; auto.
  replace (2*S p) with (S (S (2*p))) by .
  unfold ; fold ; rewrite div2_2p0; auto.
Qed.


Fixpoint p :=
  match p with
    | 0 1
    | S p 2* p
  end.

Fact pow2_fix0 : 0 = 1.
Proof. reflexivity. Qed.

Fact pow2_fix1 p : (S p) = 2* p.
Proof. reflexivity. Qed.

Fact pow2_ge1 p : 1 p.
Proof. induction p; simpl; . Qed.

Fact pow2_2n1_dec n : { p : & { b | S n = p*(2*b+1) } }.
Proof.
  induction on n as IH with measure n.
  generalize (div2_spec (S n)).
  destruct ( (S n)) as (d,[]); intros Hn.
  + exists 0, d; simpl; .
  + destruct d as [ | d ].
    * simpl in Hn; .
    * destruct (IH d) as (p & b & H).
      - .
      - exists (S p), b; rewrite pow2_fix1, mult_assoc, H; auto.
Qed.


Fact pow2_dec_uniq p a q b : p*(2*a+1) = q*(2*b+1) p = q a = b.
Proof.
  revert q; induction p as [ | p IHp ]; intros [ | q ].
  + simpl; .
  + rewrite pow2_fix0, pow2_fix1, mult_assoc; .
  + rewrite pow2_fix0, pow2_fix1, mult_assoc; .
  + rewrite !pow2_fix1, !mult_assoc; intros H.
    destruct (IHp q); .
Qed.


Fact pow2_dec_ge1 p b : 1 p*(2*b+1).
Proof.
  change 1 with (1*1) at 1; apply mult_le_compat;
    try ; apply pow2_ge1.
Qed.


Section pow2_bound.

  Let loop := fix loop x n :=
    match n with
      | 0 0
      | S n match x with
                 | (0,_) 0
                 | (p,_) S (loop p n)
               end
    end.

  Let loop_prop n : x, x < n x < (S (loop x n)).
  Proof.
    induction n as [ | n IHn ]; intros x Hx.
    { . }
    unfold loop; fold loop.
    generalize (div2_spec x).
    destruct ( x) as ([ | p ],[]); intros H.
    + simpl; .
    + simpl; .
    + specialize (IHn (S p)); spec in IHn.
      * .
      * simpl in IHn |- *; .
    + specialize (IHn (S p)); spec in IHn.
      * .
      * simpl in IHn |- *; .
  Qed.


  Definition find_pow2 x := S (loop (pred x) x).

  Fact find_pow2_geq x : 1 find_pow2 x.
  Proof. unfold find_pow2; . Qed.

  Fact find_pow2_prop x : x (find_pow2 x).
  Proof.
    unfold find_pow2; destruct x.
    + simpl; .
    + apply loop_prop; auto.
  Qed.


End pow2_bound.

Section nat_sorted.

  Definition nat_sorted ll := l a m b r, ll = l a :: m b :: r a < b.

  Fact in_nat_sorted_0 : nat_sorted nil.
  Proof. intros [] ? ? ? ? ?; discriminate. Qed.

  Fact in_nat_sorted_1 x : nat_sorted (x::nil).
  Proof. intros [ | ? [] ] ? [] ? ? ?; discriminate. Qed.

  Fact in_nat_sorted_2 x y ll : x < y nat_sorted (y::ll) nat_sorted (x::y::ll).
  Proof.
    intros l a m b r .
    destruct l as [ | u l ].
    inversion ; subst.
    destruct m as [ | v m ].
    inversion ; subst; auto.
    inversion ; subst.
    apply lt_trans with (1 := ), ( nil _ m _ r); auto.
    inversion ; subst.
    apply ( l _ m _ r); auto.
  Qed.


  Fact in_nat_sorted_3 x ll : Forall (lt x) ll nat_sorted ll nat_sorted (x::ll).
  Proof.
    induction 1 as [ | y ll Hll IHl ].
    intro; apply in_nat_sorted_1.
    intros H.
    apply in_nat_sorted_2; auto.
  Qed.


  Fact nat_sorted_cons_inv x ll : nat_sorted (x::ll) nat_sorted ll.
  Proof. intros H l a m b r ?; apply (H (x::l) _ m _ r); subst; solve list eq. Qed.

  Fact nat_sorted_Forall x ll : nat_sorted (x::ll) Forall (lt x) ll.
  Proof.
    rewrite Forall_forall; intros H y Hy.
    apply in_split in Hy.
    destruct Hy as (l & r & ?); subst.
    apply (H nil _ l _ r); auto.
  Qed.


  Fact nat_sorted_head_inv x y ll : nat_sorted (x::y::ll) x < y.
  Proof. intros H; apply (H nil _ nil _ ll); solve list eq. Qed.

  Variable P : list Type.

  Hypothesis ( : P nil).
  Hypothesis ( : x, P (x::nil)).
  Hypothesis ( : x y l, x < y P (y::l) P (x::y::l)).

  Theorem nat_sorted_rect l : nat_sorted l P l.
  Proof.
    induction l as [ [ | x [ | y l ] ] IHl ] using (measure_rect (@length _)).
    intro; apply .
    intro; apply .
    intros H; apply .
    revert H; apply nat_sorted_head_inv.
    apply IHl.
    rew length; .
    revert H; apply nat_sorted_cons_inv.
  Qed.


End nat_sorted.

Fact nat_sorted_injective ll : nat_sorted ll list_injective ll.
Proof.
  intros H l a m b r E; generalize (H _ _ _ _ _ E); .
Qed.


Fixpoint nat_list_insert x l :=
  match l with
    | nil x::nil
    | y::l if x <? y then x::y::l else
              if y <? x then y::nat_list_insert x l else y::l
  end.

Fact nat_list_insert_length x l : length (nat_list_insert x l) S (length l).
Proof.
  induction l as [ | y l IHl ]; simpl.
  .
  destruct (x <? y); simpl; try .
  destruct (y <? x); simpl; .
Qed.


Fact nat_list_insert_incl x l : incl (nat_list_insert x l) (x::l)
                              incl (x::l) (nat_list_insert x l).
Proof.
  split.

  induction l as [ | y l IHl ]; simpl.
  intro; auto.
  destruct (x <? y); destruct (y <? x).
  intro; auto.
  intro; auto.
  intros ? [ [] | H ]; simpl; auto.
  apply IHl in H; simpl in H; tauto.
  intro; simpl; tauto.

  induction l as [ | y l IHl ]; simpl.
  intro; auto.
  generalize (Nat.ltb_lt x y) (Nat.ltb_lt y x).
  destruct (x <? y); destruct (y <? x); intros z; auto.
  intros [ Hz | [ Hz | Hz ] ]; subst.
  right; apply IHl; left; auto.
  left; auto.
  right; apply IHl; right; auto.
  destruct (lt_eq_lt_dec x y) as [ [ H | ] | H ].
  apply in H; discriminate.
  2: apply in H; discriminate.
  intros [ Hz | [ Hz | Hz ] ]; subst; auto.
  left; auto.
  left; auto.
  right; auto.
Qed.


Fact nat_list_insert_Forall (P : Prop) x l :
      P x Forall P l Forall P (nat_list_insert x l).
Proof.
  do 2 rewrite Forall_forall; intros y Hy.
  apply nat_list_insert_incl in Hy.
  destruct Hy; subst; auto.
Qed.


Fact nat_list_insert_sorted x l : nat_sorted l nat_sorted (nat_list_insert x l).
Proof.
  induction l as [ | y l IHl ]; simpl.
  intro; apply in_nat_sorted_1.
  intros H.
  generalize (Nat.ltb_lt x y) (Nat.ltb_lt y x).
  destruct (x <? y); destruct (y <? x); intros .
  apply proj1 in ; spec in ; auto.
  apply proj1 in ; spec in ; auto.
  .
  apply in_nat_sorted_2; auto; tauto.
  apply in_nat_sorted_3.
  apply nat_list_insert_Forall.
  tauto.
  apply nat_sorted_Forall; auto.
  apply IHl; revert H; apply nat_sorted_cons_inv.
  auto.
Qed.


Definition nat_sort := fold_right (nat_list_insert) nil.

Fact nat_sort_length l : length (nat_sort l) length l.
Proof.
  induction l as [ | x l IHl ]; simpl.
  .
  apply le_trans with (1 := nat_list_insert_length _ _); .
Qed.


Fact nat_sort_eq l : incl (nat_sort l) l incl l (nat_sort l).
Proof.
  induction l as [ | x l IHl ]; simpl; split; intros y Hy; auto.
  apply nat_list_insert_incl in Hy; simpl.
  destruct Hy as [ | Hy ]; auto; right; apply IHl; auto.
  apply nat_list_insert_incl.
  destruct Hy; [ left | right ]; auto.
  apply IHl; auto.
Qed.


Fact nat_sort_sorted l : nat_sorted (nat_sort l).
Proof.
  induction l as [ | x l IHl ].
  apply in_nat_sorted_0.
  simpl; apply nat_list_insert_sorted; auto.
Qed.


Fact nat_sinc (f : ) a b :
      ( x, a x < b f x < f (S x))
    ( x y, a x < y y b f x < f y).
Proof.
  intros .
  assert ( n m, n m b - a f (a+n) f (a+m)) as .
    intros n m ( & ); revert .
    induction 1 as [ | m Hm IH ]; auto.
    intros H. spec in IH. .
    apply le_trans with (1 := IH).
    replace (a+S m) with (S (a+m)) by .
    apply lt_le_weak, ; .
  assert ( n m, n < m b - a f (a+n) < f (a+m)) as .
    unfold lt at 1; intros n m H.
    specialize ( (a+n)).
    spec in .
    .
    apply lt_le_trans with (1 := ).
    replace (S (a+n)) with (a+S n) by .
    apply ; auto.
  intros x y .
  replace x with (a+(x-a)) by .
  replace y with (a+(y-a)) by .
  apply .
  .
Qed.


Fact nat_sinc_inj f a b :
      ( x y, a x < y y b f x < f y)
    ( x y, a x b a y b f x = f y x = y).
Proof.
  intros x y Hx Hy.
  destruct Hx; destruct Hy.
  destruct (lt_eq_lt_dec x y) as [ [ ? | ? ] | ? ]; auto.
  specialize ( x y).
  spec in ; repeat split; auto; intro; .
  specialize ( y x).
  spec in ; repeat split; auto; intro; .
Qed.


Theorem nat_rev_ind (P : Prop) (HP : n, P (S n) P n) x y : x y P y P x.
Proof. induction 1; auto. Qed.

Section nat_rev_bounded_ind.

  Variables (k : ) (P : Prop) (HP : n, S n k P (S n) P n).

  Fact nat_rev_bounded_ind x y : x y k P y P x.
  Proof.
    intros .
    refine (proj1 (@nat_rev_ind ( n P n n k) _ x y _ _)).
    clear x y ; intros n ( & ); split; auto; .
    .
    split; auto; .
  Qed.


End nat_rev_bounded_ind.

Section nat_minimize.

  Variable P : Prop.
  Hypothesis HP : n, { P n } + { P n }.

  Inductive bar_min (n : ) : Prop :=
    | in_bar_min_0 : P n bar_min n
    | in_bar_min_1 : bar_min (S n) bar_min n.

  Section nat_min.

    Let min_rec : n, bar_min n { m | P m x, P x x < n m x }.
    Proof.
      refine (fix loop n Hn := match HP n with
        | left H exist _ n _
        | right H match loop (S n) _ with
          | exist _ m Hm exist _ m _
        end
      end).
      * split; auto; intros; .
      * destruct Hn; auto; destruct H; auto.
      * destruct Hm as [ ]; split; auto.
        intros x Hx; specialize ( x Hx).
        destruct (eq_nat_dec x n).
        - subst; tauto.
        - .
    Qed.


    Definition min_dec : ( n, P n) { m | P m x, P x m x }.
    Proof.
      intros H.
      destruct (@min_rec 0) as (m & & ).
      * destruct H as (n & Hn).
        apply in_bar_min_0 in Hn.
        revert Hn; apply nat_rev_ind.
        apply in_bar_min_1.
        .
      * exists m; split; auto.
        intros x Hx; specialize ( _ Hx); .
    Defined.


  End nat_min.

  Fact first_which : ( x, P x) { m | P m x, x < m P x }.
  Proof.
    intros H.
    destruct (min_dec H) as (m & & ).
    exists m; split; auto.
    intros x Hx .
    apply in .
    .
  Qed.


End nat_minimize.

Section first_which_ni.

  Variable P : Prop.

  Fact bounded_search_ni n : ( i, i < n P i P i) ( i, i < n P i) i, i < n P i j, j < i P j.
  Proof.
    revert P; induction n as [ | n IHn ]; intros P HP.
    + left; intros; .
    + destruct (HP 0) as [ H | H ]; try .
      - right; exists 0; split; try ; split; auto; intros; .
      - destruct IHn with (P := n P (S n)) as [ | (x & & & ) ].
        * intros; apply HP; .
        * left; intros [] ?; auto; apply ; .
        * right; exists (S x); split; try ; split; auto.
          intros [] ?; auto; apply ; .
  Qed.


  Hypothesis HP : n, P n P n.

  Fact first_which_ni : ( x, P x) m, P m x, x < m P x.
  Proof.
    intros (n & Hn).
    destruct (@bounded_search_ni (S n)) as [ | (m & & & ) ].
    + intros; auto.
    + contradict Hn; apply ; .
    + exists m; auto.
  Qed.


End first_which_ni.