Require Import List Arith Nat Lia Max Wellfounded Coq.Setoids.Setoid.

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

From Undecidability.Shared.Libs.DLW.Vec
  Require Import pos vec.

From Undecidability.TRAKHTENBROT
  Require Import notations fol_ops fo_sig fo_terms fo_logic.

Set Implicit Arguments.


Section membership.


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

  Infix "∈" := mem.
  Notation "x ∉ y" := (~ x y).

  Definition mb_incl x y := forall a, a x -> a y.
  Definition mb_equiv x y := forall a, a x <-> a y.

  Infix "⊆" := mb_incl.
  Infix "≈" := mb_equiv.
  Notation "x ≉ y" := (~ x y).

  Definition mb_transitive t := forall x y, x y -> y t -> x t.


  Variable (Rdec : forall x y, { x y } + { x y }) (Xfin : finite_t X).

  Let lX : list X := proj1_sig Xfin.
  Let HX : forall x, In x lX := proj2_sig Xfin.

  Fact mb_incl_refl x : x x.
  Proof. red; auto. Qed.

  Fact mb_incl_trans x y z : x y -> y z -> x z.
  Proof. unfold mb_incl; auto. Qed.

  Fact mb_incl_choose x y : { z | z x /\ z y } + { x y }.
  Proof.
    set (P z := z x /\ z y).
    set (Q z := z x -> z y).
    destruct list_dec with (P := P) (Q := Q) (l := lX)
      as [ (z & _ & H2 & H3) | H ]; unfold P, Q in *; clear P Q.
    + intros z; destruct (Rdec z x); destruct (Rdec z y); tauto.
    + left; exists z; auto.
    + right; intros z; apply H; auto.
  Qed.

  Fact mb_incl_dec x y : { x y } + { ~ x y }.
  Proof.
    destruct (mb_incl_choose x y) as [ (?&?&?) |]; auto.
  Qed.

  Fact mb_equiv_eq x y : x y <-> x y /\ y x.
  Proof. firstorder. Qed.

  Fact mb_equiv_dec x y : { x y } + { x y }.
  Proof.
    destruct (mb_incl_dec x y); [ destruct (mb_incl_dec y x) | ].
    1: left; apply mb_equiv_eq; auto.
    all: right; rewrite mb_equiv_eq; tauto.
  Qed.

  Hint Resolve mb_equiv_dec : core.

  Fact mb_equiv_refl_True x : x x <-> True. Proof. unfold mb_equiv; tauto. Qed.
  Fact mb_equiv_refl x : x x. Proof. unfold mb_equiv; tauto. Qed.

  Fact mb_equiv_sym x y : x y -> y x.
  Proof. do 2 rewrite mb_equiv_eq; tauto. Qed.

  Fact mb_equiv_trans x y z : x y -> y z -> x z.
  Proof. repeat rewrite mb_equiv_eq; unfold mb_incl; intros [] []; split; auto. Qed.

  Add Parametric Relation: (X) (mb_equiv)
      reflexivity proved by mb_equiv_refl
      symmetry proved by mb_equiv_sym
      transitivity proved by mb_equiv_trans
    as mb_equiv_equivalence.

  Hint Resolve mb_equiv_refl mb_equiv_sym : core.


  Definition mb_member_ext := forall x y z, x y -> x z -> y z.

  Variable (mb_axiom_ext : mb_member_ext).

  Fact mb_equiv_mem x y : x y -> forall z, x z <-> y z.
  Proof. split; apply mb_axiom_ext; auto. Qed.

   Add Parametric Morphism: (mem) with signature
     (mb_equiv) ==> (mb_equiv) ==> (iff) as mb_mem_congruence.
  Proof.
    intros x y H1 a b H2; red in H1, H2; split;
     rewrite <- H2; apply mb_axiom_ext; auto.
  Qed.

  Add Parametric Morphism: (fun x y => x y) with signature
     (mb_equiv) ==> (mb_equiv) ==> (iff) as mb_incl_congruence.
  Proof.
    intros x y H1 a b H2; split; intros H z.
    + rewrite <- H1, <- H2; auto.
    + rewrite H1, H2; auto.
  Qed.

  Definition mb_is_pair p x y := forall a, a p <-> a x \/ a y.

  Fact mb_is_pair_comm p x y : mb_is_pair p x y -> mb_is_pair p y x.
  Proof. unfold mb_is_pair; apply forall_equiv; intro; tauto. Qed.

  Add Parametric Morphism: (mb_is_pair) with signature
     (mb_equiv) ==> (mb_equiv) ==> (mb_equiv) ==> (iff) as mb_is_pair_congruence.
  Proof.
    intros p q H1 x x' H2 y y' H3.
    apply forall_equiv; intros a.
    rewrite H1, H2, H3; tauto.
  Qed.

  Fact mb_is_pair_fun p q x y : mb_is_pair p x y -> mb_is_pair q x y -> p q.
  Proof. intros H1 H2; red in H1, H2; intro; rewrite H1, H2; tauto. Qed.


  Fact mb_is_pair_inj p x y x' y' : mb_is_pair p x y
                                 -> mb_is_pair p x' y'
                                 -> x x' /\ y y'
                                 \/ x y' /\ y x'.
  Proof.
    unfold mb_is_pair; intros H1 H2.
    generalize (proj1 (H2 x)) (proj1 (H2 y)); rewrite H1, H1, mb_equiv_refl_True, mb_equiv_refl_True.
    generalize (proj1 (H1 x')) (proj1 (H1 y')); rewrite H2, H2, mb_equiv_refl_True, mb_equiv_refl_True.
    intros [] [] [] []; auto.
  Qed.

  Fact mb_is_pair_inj' p x y : mb_is_pair p x x
                            -> mb_is_pair p y y
                            -> x y.
  Proof.
    intros H1 H2; generalize (mb_is_pair_inj H1 H2); tauto.
  Qed.

  Fact mb_is_pair_dec p x y : { mb_is_pair p x y } + { ~ mb_is_pair p x y }.
  Proof.
    unfold mb_is_pair.
    apply (fol_quant_sem_dec fol_fa); auto; intros u.
    apply fol_equiv_dec; auto.
    apply (fol_bin_sem_dec fol_disj); auto.
  Qed.

  Hint Resolve mb_is_pair_dec : core.


  Definition mb_is_opair p x y := exists a b, mb_is_pair a x x /\ mb_is_pair b x y /\ mb_is_pair p a b.

  Add Parametric Morphism: (mb_is_opair) with signature
     (mb_equiv) ==> (mb_equiv) ==> (mb_equiv) ==> (iff) as mb_is_opair_congruence.
  Proof.
    intros p q H1 x x' H2 y y' H3.
    apply exists_equiv; intros a.
    apply exists_equiv; intros b.
    rewrite H1, H2, H3; tauto.
  Qed.

  Fact mb_is_opair_fun p q x y : mb_is_opair p x y -> mb_is_opair q x y -> p q.
  Proof.
    intros (a & b & H1 & H2 & H3) (u & v & G1 & G2 & G3).
    generalize (mb_is_pair_fun H1 G1) (mb_is_pair_fun H2 G2); intros E1 E2.
    rewrite E1, E2 in H3.
    revert H3 G3; apply mb_is_pair_fun.
  Qed.

  Fact mb_is_opair_inj p x y x' y' : mb_is_opair p x y
                                  -> mb_is_opair p x' y'
                                  -> x x' /\ y y'.
  Proof.
    intros (a & b & H1 & H2 & H3) (u & v & G1 & G2 & G3).
    generalize (mb_is_pair_inj H3 G3); intros [ (E1 & E2) | (E1 & E2) ].
    + rewrite E1 in H1; rewrite E2 in H2.
      generalize (mb_is_pair_inj' H1 G1); intros E; split; auto.
      rewrite E in H2.
      generalize (mb_is_pair_inj H2 G2); intros [ | (E3 & E4) ]; try tauto.
      rewrite E4; auto.
    + rewrite E1 in H1; rewrite E2 in H2.
      generalize (mb_is_pair_inj H2 G1) (mb_is_pair_inj H1 G2).
      intros [ (E3 & E4) | (E3 & E4) ] [ (E5 & E6) | (E5 & E6) ];
        rewrite E4, <- E5; auto.
  Qed.

  Fact mb_is_opair_dec p x y : { mb_is_opair p x y } + { ~ mb_is_opair p x y }.
  Proof.
    unfold mb_is_opair.
    do 2 (apply (fol_quant_sem_dec fol_ex); auto; intro).
    repeat (apply (fol_bin_sem_dec fol_conj); auto).
  Qed.

  Hint Resolve mb_is_opair_dec : core.


  Definition mb_is_otriple t x y z := exists p, mb_is_opair p x y /\ mb_is_opair t p z.

  Add Parametric Morphism: (mb_is_otriple) with signature
     (mb_equiv) ==> (mb_equiv) ==> (mb_equiv) ==> (mb_equiv) ==> (iff) as mb_is_otriple_congruence.
  Proof.
    intros p q H1 x x' H2 y y' H3 z z' H4.
    unfold mb_is_otriple.
    apply exists_equiv; intros t.
    rewrite H1, H2, H3, H4; tauto.
  Qed.

  Fact mb_is_otriple_fun p q x y z : mb_is_otriple p x y z -> mb_is_otriple q x y z -> p q.
  Proof.
    intros (t1 & H1 & H2) (t2 & H3 & H4).
    generalize (mb_is_opair_fun H1 H3); intros E.
    rewrite E in H2.
    apply (mb_is_opair_fun H2 H4).
  Qed.

  Fact mb_is_otriple_inj t x y z x' y' z' :
             mb_is_otriple t x y z
          -> mb_is_otriple t x' y' z'
          -> x x' /\ y y' /\ z z'.
  Proof.
    intros (p & H1 & H2) (q & H3 & H4).
    destruct (mb_is_opair_inj H2 H4) as (H5 & H6).
    rewrite H5 in H1.
    generalize (mb_is_opair_inj H1 H3); tauto.
  Qed.

  Fact mb_is_otriple_dec p x y z : { mb_is_otriple p x y z } + { ~ mb_is_otriple p x y z }.
  Proof.
    apply (fol_quant_sem_dec fol_ex); auto; intro.
    repeat (apply (fol_bin_sem_dec fol_conj); auto).
  Qed.

  Hint Resolve mb_is_otriple_dec : core.


  Fixpoint mb_is_tuple t n (v : vec X n) :=
    match v with
      | vec_nil => forall z, z t
      | x##v => exists t', mb_is_opair t x t' /\ mb_is_tuple t' v
    end.

  Fact mb_is_tuple_congr p q n v : p q -> @mb_is_tuple p n v -> mb_is_tuple q v.
  Proof.
    revert p q; induction v as [ | n x v IHv ]; intros p q.
    + simpl; intros E H x; rewrite <- E; auto.
    + intros E (t & H1 & H2); exists t; split; auto.
      rewrite <- E; auto.
  Qed.

  Fact mb_is_tuple_fun p q n v : @mb_is_tuple p n v -> mb_is_tuple q v -> p q.
  Proof.
    revert p q; induction v as [ | n x v IHv ]; intros p q.
    + simpl; intros H1 H2.
      apply mb_equiv_eq; split.
      * intros z Hz; apply H1 in Hz; tauto.
      * intros z Hz; apply H2 in Hz; tauto.
    + intros (p' & H1 & H2) (q' & H3 & H4).
      generalize (IHv _ _ H2 H4); intros E.
      rewrite E in H1.
      revert H1 H3; apply mb_is_opair_fun.
  Qed.

  Fact mb_is_tuple_inj t n v w :
             @mb_is_tuple t n v
          -> mb_is_tuple t w
          -> forall p, vec_pos v p vec_pos w p.
  Proof.
    revert t w; induction v as [ | n x v IHv ]; intros t w.
    + intros _ _ p; invert pos p.
    + vec split w with y.
      intros (p & H1 & H2) (q & H3 & H4).
      destruct (mb_is_opair_inj H1 H3) as (E1 & E2).
      apply mb_is_tuple_congr with (1 := E2) in H2.
      specialize (IHv _ _ H2 H4).
      intros j; invert pos j; auto.
  Qed.

  Fact mb_is_tuple_dec t n v : { @mb_is_tuple t n v } + { ~ mb_is_tuple t v }.
  Proof.
    revert t; induction v as [ | x n v IHv ]; intros t.
    + apply (fol_quant_sem_dec fol_fa); auto; intro.
      apply (fol_bin_sem_dec fol_imp); auto.
    + simpl; apply (fol_quant_sem_dec fol_ex); auto; intro.
      apply (fol_bin_sem_dec fol_conj); auto.
  Qed.

  Hint Resolve mb_is_tuple_dec : core.


  Definition mb_has_pairs (l : X) :=
     forall x y, x l -> y l -> exists p, mb_is_pair p x y.

  Definition mb_has_otriples (l : X) :=
    forall x y z, x l -> y l -> z l -> exists t, mb_is_otriple t x y z.

  Definition mb_has_tuples (l : X) n :=
    forall v, (forall p, vec_pos v p l) -> exists t, @mb_is_tuple t n v.

  Definition mb_is_otriple_in r x y z :=
    exists t, mb_is_otriple t x y z /\ t r.

  Definition mb_is_tuple_in r n v :=
    exists t, @mb_is_tuple t n v /\ t r.

  Fact mb_is_tuple_in_congr x y n v : y x -> @mb_is_tuple_in x n v -> @mb_is_tuple_in y n v.
  Proof.
    intros E (t & H1 & H2); exists t; split; auto.
    rewrite E; auto.
  Qed.

  Fact mb_is_otriple_in_dec r x y z : { mb_is_otriple_in r x y z } + { ~ mb_is_otriple_in r x y z }.
  Proof.
    apply (fol_quant_sem_dec fol_ex); auto; intro.
    repeat (apply (fol_bin_sem_dec fol_conj); auto).
  Qed.

  Fact mb_is_tuple_in_dec r n v : { @mb_is_tuple_in r n v } + { ~ mb_is_tuple_in r v }.
  Proof.
    apply (fol_quant_sem_dec fol_ex); auto; intro.
    apply (fol_bin_sem_dec fol_conj); auto.
  Qed.


  Definition mb_is_tot n (l s : X) :=
    forall v, (forall p, vec_pos v p l) -> exists x p t, x l /\ p s /\ mb_is_opair p x t /\ @mb_is_tuple t n v.

  Definition mb_is_fun (l s : X) :=
    forall p q x x' y, x l -> x' l
                    -> p s -> q s
                    -> mb_is_opair p x y
                    -> mb_is_opair q x' y
                    -> x x'.

End membership.

Section FOL_encoding.



  Notation Σ2 := (Σrel 2).
  Variable (Y : Type) (M2 : fo_model Σ2 Y).

  Let mem a b := fom_rels M2 tt (a##b##ø).
  Infix "∈m" := mem (at level 59, no associativity).

  Definition Σ2_mem x y := @fol_atom Σ2 tt (£x##£y##ø).
  Infix "∈" := Σ2_mem.

  Definition Σ2_non_empty l := 0 (1+l).
  Definition Σ2_incl x y := 0 (S x) 0 (S y).
  Definition Σ2_equiv x y := 0 (S x) 0 (S y).

  Infix "⊆" := Σ2_incl.
  Infix "≈" := Σ2_equiv.

  Definition Σ2_transitive t := ∀∀ 1 0 0 (2+t) 1 (2+t).

  Definition Σ2_extensional := ∀∀∀ 2 1 2 0 1 0.

  Definition Σ2_is_pair p x y := 0 (S p) 0 S x 0 S y.

  Definition Σ2_is_opair p x y :=
         ∃∃ Σ2_is_pair 1 (2+x) (2+x)
             Σ2_is_pair 0 (2+x) (2+y)
             Σ2_is_pair (2+p) 1 0.

  Fact Σ2_is_opair_vars p x y : incl (fol_vars (Σ2_is_opair p x y)) (p::x::y::nil).
  Proof. cbv; tauto. Qed.

  Definition Σ2_is_otriple p x y z :=
           Σ2_is_opair 0 (S x) (S y)
             Σ2_is_opair (S p) 0 (S z).

  Definition Σ2_is_otriple_in r x y z :=
           Σ2_is_otriple 0 (S x) (S y) (S z)
             0 (S r).

  Definition Σ2_has_otriples l :=
        ∀∀∀ 2 (3+l)
                       1 (3+l)
                       0 (3+l)
                       Σ2_is_otriple 0 3 2 1.

  Fixpoint Σ2_is_tuple t n : vec nat n -> fol_form Σ2 :=
    match n with
      | 0 => fun _ => 0 (S t)
      | S n => fun v => Σ2_is_opair (S t) (S (vec_head v)) 0
                             Σ2_is_tuple 0 (vec_map S (vec_tail v))
    end.

  Fact Σ2_is_tuple_vars t n v : incl (fol_vars (@Σ2_is_tuple t n v)) (t::vec_list v).
  Proof.
    revert t v; induction n as [ | n IHn ]; intros t v.
    + vec nil v; cbv; tauto.
    + vec split v with x; simpl Σ2_is_tuple.
      intros i; rewrite fol_vars_quant, in_flat_map.
      intros (j & H1 & H2).
      rewrite fol_vars_bin, in_app_iff in H1.
      destruct H1 as [ H1 | H1 ].
      * apply Σ2_is_opair_vars in H1.
        destruct H1 as [ | [ | [ | [] ] ] ]; subst j; simpl in *; tauto.
      * apply IHn in H1.
        destruct H1 as [ <- | H1 ].
        - simpl in *; tauto.
        - rewrite vec_list_vec_map, in_map_iff in H1.
          destruct H1 as (y & <- & H1); simpl in *.
          destruct H2 as [ -> | [] ]; tauto.
  Qed.

  Definition Σ2_is_tuple_in r n v := @Σ2_is_tuple 0 n (vec_map S v) 0 (S r).

  Fact Σ2_is_tuple_in_vars r n v : incl (fol_vars (@Σ2_is_tuple_in r n v)) (r::vec_list v).
  Proof.
    unfold Σ2_is_tuple_in.
    intros x; rewrite fol_vars_quant, in_flat_map.
    intros (y & H1 & H2).
    rewrite fol_vars_bin, in_app_iff in H1.
    destruct H1 as [ H1 | H1 ].
    + apply Σ2_is_tuple_vars in H1.
      rewrite vec_list_vec_map in H1; simpl in H1.
      rewrite in_map_iff in H1.
      destruct H1 as [ <- | (z & <- & H1) ]; simpl in *; try tauto.
      destruct H2 as [ <- | [] ]; auto.
    + simpl in H1.
      destruct H1 as [ <- | [ <- | [] ] ]; simpl in *; tauto.
  Qed.

  Definition Σ2_has_tuples l n :=
       fol_mquant fol_fa n ( (fol_vec_fa (vec_set_pos (fun p : pos n => pos2nat p (l+n))))
                                          Σ2_is_tuple 0 (vec_set_pos (fun p : pos n => S (pos2nat p)))).

  Definition Σ2_is_tot n l s :=
       fol_mquant fol_fa n ( (fol_vec_fa (vec_set_pos (fun p : pos n => pos2nat p (l+n))))
                                          ∃∃∃ 2 ((3+l)+n) 1 ((3+s)+n) Σ2_is_opair 1 2 0 @Σ2_is_tuple 0 n (vec_set_pos (fun p : pos n => 3+pos2nat p)) ).


  Definition Σ2_is_fun l s :=
    ∀∀∀∀∀ 2 (5+l) 1 (5+l)
          4 (5+s) 3 (5+s)
          Σ2_is_opair 4 2 0
          Σ2_is_opair 3 1 0
          2 1.

  Definition Σ2_list_in l lv := fol_lconj (map (fun x => x l) lv).

  Fact Σ2_is_otriple_in_vars r x y z : incl (fol_vars (Σ2_is_otriple_in r x y z)) (r::x::y::z::nil).
  Proof. intros a; simpl; tauto. Qed.

  Notation "⟪ A ⟫" := (fun ψ => fol_sem M2 ψ A).

  Section semantics.

    Fact Σ2_transitive_spec t ψ : Σ2_transitive t ψ = mb_transitive mem (ψ t).
    Proof. reflexivity. Qed.

    Fact Σ2_non_empty_spec l ψ : Σ2_non_empty l ψ = exists x, x m ψ l.
    Proof. reflexivity. Qed.

    Fact Σ2_incl_spec x y ψ : Σ2_incl x y ψ = mb_incl mem (ψ x) (ψ y).
    Proof. reflexivity. Qed.

    Fact Σ2_equiv_spec x y ψ : Σ2_equiv x y ψ = mb_equiv mem (ψ x) (ψ y).
    Proof. reflexivity. Qed.

    Fact Σ2_extensional_spec ψ : Σ2_extensional ψ = mb_member_ext mem.
    Proof. reflexivity. Qed.

    Fact Σ2_is_pair_spec p x y ψ : Σ2_is_pair p x y ψ = mb_is_pair mem (ψ p) (ψ x) (ψ y).
    Proof. reflexivity. Qed.

    Fact Σ2_is_opair_spec p x y ψ : Σ2_is_opair p x y ψ = mb_is_opair mem (ψ p) (ψ x) (ψ y).
    Proof. reflexivity. Qed.

    Fact Σ2_is_otriple_spec p x y z ψ : Σ2_is_otriple p x y z ψ = mb_is_otriple mem (ψ p) (ψ x) (ψ y) (ψ z).
    Proof. reflexivity. Qed.

    Fact Σ2_is_otriple_in_spec r x y z ψ : Σ2_is_otriple_in r x y z ψ = mb_is_otriple_in mem (ψ r) (ψ x) (ψ y) (ψ z).
    Proof. reflexivity. Qed.

    Fact Σ2_has_otriples_spec l ψ : Σ2_has_otriples l ψ = mb_has_otriples mem (ψ l).
    Proof. reflexivity. Qed.

    Fact Σ2_is_tuple_spec t n v ψ : @Σ2_is_tuple t n v ψ <-> mb_is_tuple mem (ψ t) (vec_map ψ v).
    Proof.
      induction n as [ | n IHn ] in t, v, ψ |- *.
      + vec nil v; reflexivity.
      + vec split v with x.
        simpl Σ2_is_tuple.
        simpl mb_is_tuple.
        rewrite fol_sem_quant_fix.
        apply (fol_quant_sem_ext fol_ex); intros y.
        rewrite fol_sem_bin_fix.
        apply fol_bin_sem_ext.
        * reflexivity.
        * rewrite IHn, vec_map_map; reflexivity.
    Qed.

    Fact Σ2_is_tuple_in_spec r n v ψ : @Σ2_is_tuple_in r n v ψ <-> mb_is_tuple_in mem (ψ r) (vec_map ψ v).
    Proof.
      simpl; apply (fol_quant_sem_ext fol_ex); intros y.
      apply (fol_bin_sem_ext fol_conj).
      + rewrite Σ2_is_tuple_spec, vec_map_map; simpl; reflexivity.
      + reflexivity.
    Qed.

    Fact Σ2_has_tuples_spec l n ψ : Σ2_has_tuples l n ψ <-> mb_has_tuples mem (ψ l) n.
    Proof.
      unfold Σ2_has_tuples.
      rewrite fol_sem_mforall.
      apply (fol_quant_sem_ext fol_fa); intros v.
      rewrite fol_sem_bin_fix.
      apply (fol_bin_sem_ext fol_imp).
      + rewrite fol_sem_vec_fa.
        apply (fol_quant_sem_ext fol_fa); intros p.
        rew vec.
        simpl.
        rewrite env_vlift_fix0, env_vlift_fix1.
        reflexivity.
      + rewrite fol_sem_quant_fix.
        apply (fol_quant_sem_ext fol_ex); intros x.
        rewrite Σ2_is_tuple_spec; simpl.
        apply fol_equiv_ext; f_equal.
        apply vec_pos_ext; intros p.
        rew vec; simpl.
        rewrite env_vlift_fix0; auto.
    Qed.

    Fact Σ2_is_fun_spec l s ψ : Σ2_is_fun l s ψ = mb_is_fun mem (ψ l) (ψ s).
    Proof. reflexivity. Qed.

    Fact Σ2_is_tot_spec n l s ψ : Σ2_is_tot n l s ψ <-> mb_is_tot mem n (ψ l) (ψ s).
    Proof.
      unfold Σ2_is_tot, mb_is_tot.
      rewrite fol_sem_mforall.
      apply forall_equiv; intros v.
      rewrite fol_sem_bin_fix.
      apply (fol_bin_sem_ext fol_imp).
      + rewrite fol_sem_vec_fa.
        apply forall_equiv; intros p.
        rew vec.
        simpl; rewrite env_vlift_fix0, env_vlift_fix1; tauto.
      + rewrite fol_sem_quant_fix; apply exists_equiv; intros x.
        rewrite fol_sem_quant_fix; apply exists_equiv; intros p.
        rewrite fol_sem_quant_fix; apply exists_equiv; intros t.
        do 3 (rewrite fol_sem_bin_fix).
        repeat apply (fol_bin_sem_ext fol_conj).
        * simpl; rewrite env_vlift_fix1; tauto.
        * simpl; rewrite env_vlift_fix1; tauto.
        * rewrite Σ2_is_opair_spec; simpl; tauto.
        * rewrite Σ2_is_tuple_spec; simpl.
          apply fol_equiv_ext; f_equal.
          apply vec_pos_ext; intros q; rew vec.
          simpl; rewrite env_vlift_fix0; auto.
    Qed.

    Fact Σ2_list_in_spec l lv ψ : Σ2_list_in l lv ψ <-> forall x, In x lv -> ψ x m ψ l.
    Proof.
      unfold Σ2_list_in; rewrite fol_sem_lconj.
      split.
      + intros H x Hx.
        apply (H (_ _)), in_map_iff.
        exists x; auto.
      + intros H f; rewrite in_map_iff.
        intros (x & <- & ?); apply H; auto.
    Qed.

  End semantics.

  Fact Σ2_is_otriple_in_equiv r x y z φ ψ :
               Σ2_is_otriple_in 3 2 1 0 z·y·x·r·φ
           <-> Σ2_is_otriple_in 3 2 1 0 z·y·x·r·ψ.
  Proof. cbv beta; do 2 rewrite Σ2_is_otriple_in_spec; simpl; tauto. Qed.

End FOL_encoding.