Require Import Relations.
Require Import FSets.
Require Import Arith.
Require Import Lia.

Set Keyed Unification.

Lemma Bool_elim_bool : forall (b:bool),b=true \/ b=false.
  destruct b;try tauto.
Qed.

Module OrderedPair (X:OrderedType) (Y:OrderedType) <: OrderedType with
Definition t := (X.t * Y.t)%type.
  Definition t := (X.t * Y.t)%type.

  Definition eq (xy1:t) (xy2:t) :=
    let (x1,y1) := xy1 in
      let (x2,y2) := xy2 in
        (X.eq x1 x2) /\ (Y.eq y1 y2).

  Definition lt (xy1:t) (xy2:t) :=
    let (x1,y1) := xy1 in
      let (x2,y2) := xy2 in
        (X.lt x1 x2) \/ ((X.eq x1 x2) /\ (Y.lt y1 y2)).

  Lemma eq_refl : forall (x:t),(eq x x).
    destruct x.
    unfold eq.
    split;[apply X.eq_refl | apply Y.eq_refl].
  Qed.

  Lemma eq_sym : forall (x y:t),(eq x y)->(eq y x).
    destruct x;destruct y;unfold eq;intro.
    elim H;clear H;intros.
    split;[apply X.eq_sym | apply Y.eq_sym];trivial.
  Qed.

  Lemma eq_trans : forall (x y z:t),(eq x y)->(eq y z)->(eq x z).
    unfold eq;destruct x;destruct y;destruct z;intros.
    elim H;clear H;intros.
    elim H0;clear H0;intros.
    split;[eapply X.eq_trans | eapply Y.eq_trans];eauto.
  Qed.

  Lemma lt_trans : forall (x y z:t),(lt x y)->(lt y z)->(lt x z).
    unfold lt;destruct x;destruct y;destruct z;intros.
    case H;clear H;intro.
    case H0;clear H0;intro.
    left.
    eapply X.lt_trans;eauto.
    elim H0;clear H0;intros.
    left.
    case (X.compare t0 t4);trivial;intros.
    generalize (X.eq_sym H0);intro.
    generalize (X.eq_trans e H2);intro.
    elim (X.lt_not_eq H H3).
    generalize (X.lt_trans l H);intro.
    generalize (X.eq_sym H0);intro.
    elim (X.lt_not_eq H2 H3).
    elim H;clear H;intros.
    case H0;clear H0;intro.
    left.
    case (X.compare t0 t4);trivial;intros.
    generalize (X.eq_sym H);intro.
    generalize (X.eq_trans H2 e);intro.
    elim (X.lt_not_eq H0 H3).
    generalize (X.lt_trans H0 l);intro.
    generalize (X.eq_sym H);intro.
    elim (X.lt_not_eq H2 H3).
    elim H0;clear H0;intros.
    right.
    split;
    eauto with ordered_type.
  Qed.

  Lemma lt_not_eq : forall (x y:t),(lt x y)->~(eq x y).
    unfold lt, eq;destruct x;destruct y;intro;intro.
    elim H0;clear H0;intros.
    case H.
    intro.
    apply (X.lt_not_eq H2 H0).
    intro.
    elim H2;clear H2;intros.
    apply (Y.lt_not_eq H3 H1).
  Qed.

  Definition compare : forall (x y:t),(Compare lt eq x y).
    destruct x;destruct y.
    case (X.compare t0 t2);intro.
    apply LT.
    left;trivial.
    case (Y.compare t1 t3);intro.
    apply LT.
    right.
    tauto.
    apply EQ.
    split;trivial.
    apply GT.
    right;auto with ordered_type.
    apply GT.
    left;trivial.
  Defined.

  Definition eq_dec : forall (x y: t), { eq x y } + { ~ eq x y}.
  Proof.
    intros [xa xb] [ya yb]; simpl.
    destruct (X.eq_dec xa ya).
    destruct (Y.eq_dec xb yb).
    + left; now split.
    + right. now intros [eqa eqb].
    + right. now intros [eqa eqb].
  Defined.

  Hint Immediate eq_sym.
  Hint Resolve eq_refl eq_trans lt_not_eq lt_trans.
End OrderedPair.

Module MessageSpi.
  Inductive message : Set :=
  | MNam : nat -> message.

  Definition t := message.

  Fixpoint message_lt (m n:message) {struct m} : Prop :=
    match (m,n) with
      | (MNam n1,MNam n2) => n1 < n2
    end.

  Module Ord <: OrderedType with Definition t := message with Definition eq :=
@eq message.
    Definition t := message.
    Definition eq := @eq message.
    Definition lt := message_lt.

    Lemma eq_refl : forall (x:t),eq x x.
      unfold eq;auto.
    Qed.

    Lemma eq_sym : forall (x y:t),(eq x y )->(eq y x).
      unfold eq;auto.
    Qed.

    Lemma eq_trans : forall (x y z:t),(eq x y)->(eq y z)->(eq x z).
      unfold eq;auto;intros;congruence.
    Qed.

    Lemma lt_trans : forall (x y z:t),(lt x y)->(lt y z)->(lt x z).
      unfold lt.
      induction x;destruct y;simpl;try tauto;destruct z;try tauto;intros.
      lia.
    Qed.

    Lemma lt_not_eq : forall (x y:t),(lt x y)->~(eq x y).
      unfold eq;unfold lt.
      induction x;destruct y;simpl;try tauto;intro;red;intro;try (discriminate
H0);injection H0;intros.
      elim (lt_irrefl n); lia.
    Qed.

    Definition compare : forall (x y:t),(Compare lt eq x y).
      unfold lt, eq.
      induction x;destruct y;intros;try (apply LT;simpl;trivial;fail);try
(apply
GT;simpl;trivial;fail).
      case (lt_eq_lt_dec n n0);intros;try (case s;clear s;intros).
      apply LT;trivial.
      apply EQ;trivial.
      rewrite e;trivial.
      apply GT;trivial.
    Defined.

    Definition eq_dec : forall (x y: t), { eq x y } + { ~ eq x y}.
    Proof.
    intros [i] [j]. unfold eq.
    destruct (eq_nat_dec i j).
    + left. now f_equal.
    + right. intros meq; now inversion meq.
    Defined.

    Hint Immediate eq_sym.
    Hint Resolve eq_refl eq_trans lt_not_eq lt_trans.
  End Ord.

  Theorem eq_dec : forall (m n:message),{m=n}+{~(m=n)}.
    intros.
    case (Ord.compare m n);intro;[right | left | right];try (red;intro).
    elim (Ord.lt_not_eq m n);auto.
    rewrite e;auto.
    elim (Ord.lt_not_eq n m);auto.
  Defined.
End MessageSpi.

Module MessagePair := OrderedPair MessageSpi.Ord MessageSpi.Ord.

Module Type Hedge := FSetInterface.S with Module E := MessagePair.

Module A (H:Hedge).
  Definition hedge := H.t.

  Definition message_relation := relation MessageSpi.message.

  Definition relation_of_hedge (h:hedge) (m n:MessageSpi.message) := H.In (m,n)
h.

  Inductive hedge_synthesis_relation (h:message_relation) : message_relation :=
    | SynInc : forall (m n:MessageSpi.message),(h m
n)->(hedge_synthesis_relation h m n).

  Fixpoint hedge_in_synthesis (h:hedge) (m:MessageSpi.message)
(n:MessageSpi.message) {struct m} : bool :=
    if H.mem (m,n) h
      then true
      else false.

  Definition hedge_synthesis_spec (h:hedge) := hedge_synthesis_relation
(relation_of_hedge h).

  Lemma hedge_in_synthesis_impl_hedge_synthesis_spec : forall (h:hedge),forall
(m n:MessageSpi.message),(hedge_in_synthesis h m n)=true->(hedge_synthesis_spec
h m n).
    unfold hedge_synthesis_spec;unfold relation_of_hedge.
    induction m;simpl;intro.
    elim (Bool_elim_bool (H.mem (MessageSpi.MNam n,n0) h));intros.
    apply SynInc;apply H.mem_2;trivial.
    rewrite H in H0.  (* !! possible here !! *)
    discriminate H0.
  Qed.
End A.

Module B (H:Hedge).
  Definition hedge := H.t.

  Definition message_relation := relation MessageSpi.t.

  Definition relation_of_hedge (h:hedge) (m n:MessageSpi.t) := H.In (m,n) h.

  Inductive hedge_synthesis_relation (h:message_relation) : message_relation :=
    | SynInc : forall (m n:MessageSpi.t),(h m n)->(hedge_synthesis_relation h m
n).

  Fixpoint hedge_in_synthesis (h:hedge) (m:MessageSpi.t) (n:MessageSpi.t)
{struct m} : bool :=
    if H.mem (m,n) h
      then true
      else false.

  Definition hedge_synthesis_spec (h:hedge) := hedge_synthesis_relation
(relation_of_hedge h).

  Lemma hedge_in_synthesis_impl_hedge_synthesis_spec : forall (h:hedge),forall
(m n:MessageSpi.t),(hedge_in_synthesis h m n)=true->(hedge_synthesis_spec h m
n).
    unfold hedge_synthesis_spec;unfold relation_of_hedge.
    induction m;simpl;intro.
    elim (Bool_elim_bool (H.mem (MessageSpi.MNam n,n0) h));intros.
    apply SynInc;apply H.mem_2;trivial.
    rewrite H in H0. discriminate. (* !! impossible here !! *)
  Qed.
End B.
