Require Import List Arith Nat Lia Relations Bool.

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

Local Infix "∈" := In (at level 70, no associativity).
Local Infix "⊆" := incl (at level 70, no associativity).
Local Notation "P ≅ Q" := ((P -> Q) * (Q -> P))%type (at level 70, no associativity).

Set Implicit Arguments.

Section finite_t_upto.

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

  Definition fin_t_upto (P : X -> Type) :=
     { l : _ & (forall x, P x -> exists y, y l /\ R x y)
              *(forall x y, x l -> R x y -> P y) }%type.

  Definition finite_t_upto :=
     { l : _ | forall x, exists y, y l /\ R x y }.

  Fact finite_t_fin_upto : finite_t_upto fin_t_upto (fun _ => True).
  Proof. split; intros []; red; eauto; firstorder. Qed.

End finite_t_upto.

Arguments finite_t_upto : clear implicits.

Section finite_t_weak_dec_powerset.


  Variable (X : Type).

  Let wdec (R : X -> Prop) := forall x, R x \/ ~ R x.

  Let pset_fin_t (l : list X) : { ll | length ll = 2 ^ (length l)
                                     /\ forall R, wdec R
                                   -> exists T, T ll
                                     /\ forall x, x l -> R x <-> T x }.
  Proof.
    induction l as [ | x l IHl ].
    + exists ((fun _ => True) :: nil); split; auto.
      intros R HR; exists (fun _ => True); simpl; split; tauto.
    + destruct IHl as (ll & Hl & Hll).
      exists (map (fun T a => x<>a /\ T a) ll ++ map (fun T a => x=a \/ T a) ll); split.
      1: rewrite app_length, !map_length; simpl; lia.
      intros R HR.
      destruct (Hll R) as (T & H1 & H2); auto.
      destruct (HR x) as [ H0 | H0 ].
      * exists (fun a => x = a \/ T a); split.
        - apply in_or_app; right.
          apply in_map_iff; exists T; auto.
        - intros y [ <- | H ]; simpl; try tauto.
          rewrite H2; auto; split; auto.
          intros [ -> | ]; auto.
          apply H2; auto.
      * exists (fun a => x <> a /\ T a); split.
        - apply in_or_app; left.
          apply in_map_iff; exists T; auto.
        - intros y [ <- | H ]; simpl; split; try tauto.
          ++ rewrite <- H2; auto; split; auto.
             contradict H0; subst; auto.
          ++ rewrite <- H2; tauto.
  Qed.


  Theorem finite_t_weak_dec_powerset (h : finite_t X) :
            { ll | length ll = 2 ^ (length (proj1_sig h))
                 /\ forall R, wdec R -> exists T, T ll /\ forall x, R x <-> T x }.
  Proof.
    destruct h as (l & Hl).
    destruct (pset_fin_t l) as (ll & H & Hll).
    exists ll; split; auto.
    intros R HR.
    destruct (Hll _ HR) as (T & H1 & H2).
    exists T; split; auto.
  Qed.

End finite_t_weak_dec_powerset.


Theorem finite_t_weak_dec_rels X :
           finite_t X
        -> { ll | forall R,
                      (forall x y : X, R x y \/ ~ R x y)
                   -> exists T, T ll /\ forall x y, R x y <-> T x y }.
Proof.
  intros h.
  destruct finite_t_weak_dec_powerset with (h := finite_t_prod h h)
    as (l & _ & Hl).
  exists (map (fun P x y => P (x,y)) l).
  intros R HR.
  destruct (Hl (fun c => R (fst c) (snd c))) as (T & H1 & H2).
  + intros []; apply HR.
  + exists (fun x y => T (x,y)); split.
    * apply in_map_iff; exists T; auto.
    * intros x y; apply (H2 (x,y)).
Qed.