(****************************************************************
   File: multiset.v                                                 
   Authors: Amy Felty
   Version: Coq V8.4pl2
   Date: January 2014
                                                                 
   A version of multisets built on lists (that doesn't require
   decidable equality).

   Adhoc for now.  Need to replace the permutation lemmas with a
   tactic to automate proofs of equality between multisets.
  ***************************************************************)

Require Export Omega.
Require Export List.
Require Export Setoid.
Require Export SetoidPermutation.
Require Export Relation_Definitions.

Set Implicit Arguments.

Section multiset.

Variable A:Type.
Variable eqA:A -> A -> Prop.
Hypothesis eqA_refl: forall t:A, eqA t t.
Hypothesis eqA_symm: forall (t1 t2:A), eqA t1 t2 -> eqA t2 t1.
Hypothesis eqA_trans:
  forall (t1 t2 t3:A), eqA t1 t2 -> eqA t2 t3 -> eqA t1 t3.

Add Parametric Relation : A eqA
  reflexivity proved by eqA_refl
  symmetry proved by eqA_symm
  transitivity proved by eqA_trans as Aeq.

Definition multiset := list A.

Definition mnil : multiset := nil.

Inductive mcount : A -> multiset -> nat -> Prop :=
   | mc_0 : forall (a:A), mcount a mnil 0
   | mc_plus0 : forall (a b:A) (m:multiset) (i:nat),
                  ~(eqA a b) -> mcount a m i -> mcount a (b::m) i
   | mc_plus1 : forall (a b:A) (m:multiset) (i:nat),
                  (eqA a b) -> mcount a m i -> mcount a (b::m) (i+1).

Definition ms_eq (m n:multiset) : Prop :=
  forall (a:A) (i:nat), (mcount a m i <-> mcount a n i).

Hint Resolve mc_0 mc_plus0 mc_plus1.

Lemma ms_eq_refl : forall (m:multiset), ms_eq m m.
Proof.
induction m.
unfold ms_eq; simpl; tauto.
unfold ms_eq in IHm.
unfold ms_eq; simpl; intros b i; subst; split; inversion_clear 1; auto.
Qed.

Lemma ms_eq_symm : forall (m n:multiset), ms_eq m n -> ms_eq n m.
Proof.
unfold ms_eq.
intros m n h a i.
split; intro h1.
generalize h; clear h.
inversion_clear 0; intro h; apply h; auto.
generalize h; clear h.
inversion_clear 0; intro h; apply h; auto.
Qed.

Lemma ms_eq_trans :
  forall (m n p:multiset), ms_eq m n -> ms_eq n p -> ms_eq m p.
Proof.
unfold ms_eq.
intros m n p h1 h2 a i.
split; intro h3.
generalize h1 h2; clear h1 h2.
inversion_clear 0; intros h1 h2; apply h2; apply h1; auto.
generalize h1 h2; clear h1 h2.
inversion_clear 0; intros h1 h2; apply h1; apply h2; auto.
Qed.

Hint Resolve ms_eq_refl ms_eq_symm ms_eq_trans.

Add Parametric Relation : multiset ms_eq
  reflexivity proved by ms_eq_refl
  symmetry proved by ms_eq_symm
  transitivity proved by ms_eq_trans as eq_ms.

Definition madd (a:A) (m:multiset) := (a::m).

Lemma madd_compat:
  forall (a b:A) (m n:multiset), eqA a b -> ms_eq m n ->
  ms_eq (madd a m) (madd b n).
Proof.
unfold madd,ms_eq.
intros a b m n h1 h2 a0 i.
split; intro h3.
inversion_clear 0.
assert (h3:~(eqA a0 b)).
rewrite h1 in H; auto.
apply mc_plus0; auto.
apply h2; auto.
rewrite h1 in H.
apply mc_plus1; auto.
apply h2; auto.
inversion_clear 0.
assert (h3:~(eqA a0 a)).
rewrite <- h1 in H; auto.
apply mc_plus0; auto.
apply h2; auto.
rewrite <- h1 in H.
apply mc_plus1; auto.
apply h2; auto.
Qed.

Add Parametric Morphism : madd with
  signature eqA ==> ms_eq ==> ms_eq as madd_mor.
Proof.
intros; apply madd_compat; auto.
Qed.

Definition msingl (a:A) := madd a mnil.

Lemma msingl_compat:
  forall (a b:A), eqA a b -> ms_eq (msingl a) (msingl b).
Proof.
unfold msingl.
intros a b h1.
rewrite h1; auto.
Qed.

Add Parametric Morphism : msingl with
  signature eqA ==> ms_eq as msingl_mor.
Proof.
exact msingl_compat.
Qed.

Definition munion (m n:multiset) := m ++ n.

Lemma munion_mcount : forall (a:A) (m n:multiset) (i j:nat),
  mcount a m i -> mcount a n j -> mcount a (munion m n) (i+j).
Proof.
intros a m n i j h.
generalize h n j; clear n j h.
induction 1.
simpl; auto.
simpl; intros n j h1; auto.
simpl; intros n j h1.
replace (i+1+j) with (i+j+1); try omega.
auto.
Qed.

Hint Resolve munion_mcount.

Lemma munion_mnil : forall (m n:multiset),
  mnil = munion m n -> m = mnil /\ n = mnil.
Proof.
unfold munion,mnil.
induction m.
simpl; auto.
intros n h.
simpl in h.
discriminate h.
Qed.

Hint Resolve munion_mnil.

Lemma mcount_munion : forall (m n:multiset) (a:A) (i:nat),
  mcount a (munion m n) i ->
  exists (j k:nat), mcount a m j /\ mcount a n k /\ i = j+k.
Proof.
unfold munion.
induction m; simpl.
intros n a i h; exists 0; exists i; repeat split; auto.
simpl; intros n a0 i h.
inversion_clear 0.
specialize IHm with (1:=H0); destruct IHm as [j [k [h1 [h2 h3]]]]; subst.
exists j; exists k; repeat split; auto.
specialize IHm with (1:=H0); destruct IHm as [j [k [h1 [h2 h3]]]]; subst.
exists (j+1); exists k; repeat split; auto; omega.
Qed.

Lemma munion_compat: forall (m1 n1:multiset), ms_eq m1 n1 ->
  forall (m2 n2:multiset), ms_eq m2 n2 -> ms_eq (munion m1 m2) (munion n1 n2).
Proof.
unfold ms_eq.
intros m1 n1 h.
assert (h1:forall (a:A)(i:nat), mcount a m1 i -> mcount a n1 i).
intros; apply h; auto.
assert (h2:forall (a:A)(i:nat), mcount a n1 i -> mcount a m1 i).
intros; apply h; auto.
clear h.
intros m2 n2 h.
assert (h3:forall (a:A)(i:nat), mcount a m2 i -> mcount a n2 i).
intros; apply h; auto.
assert (h4:forall (a:A)(i:nat), mcount a n2 i -> mcount a m2 i).
intros; apply h; auto.
clear h.
intros a i; split; intro h5.
specialize mcount_munion with (1:=h5); intros [j [k [h6 [h7 h8]]]]; subst; auto.
specialize mcount_munion with (1:=h5); intros [j [k [h6 [h7 h8]]]]; subst; auto.
Qed.

Add Parametric Morphism : munion with
  signature ms_eq ==> ms_eq ==> ms_eq as munion_mor.
Proof.
exact munion_compat.
Qed.

Definition mset_mem (a:A) (m:multiset) : Prop := In a m.

Definition ms_dec (a b:A) := (eqA a b) \/ ~(eqA a b).
Definition ms_dec3 (a b c:A) :=
  (ms_dec a b) /\ (ms_dec a c) /\ (ms_dec b c).
Definition ms_dec4 (a b c d:A) :=
  (ms_dec a b) /\ (ms_dec a c) /\ (ms_dec a d) /\ (ms_dec3 b c d).

Lemma ms_eq_cons: forall (a:A) (m n:multiset),
  ms_eq m n -> ms_eq (a::m) (a::n).
Proof.
intros a m n h.
rewrite h; auto.
Qed.

Hint Resolve ms_eq_cons.

Lemma ms_perm21 : forall (a1 a2:A) (m:multiset), ms_dec a1 a2 ->
  ms_eq (a1::a2::m) (a2::a1::m).
Proof.
intros a1 a2 m h.
change (ms_eq (munion (madd a1 (madd a2 nil)) m)
              (munion (madd a2 (madd a1 nil)) m)).
apply munion_compat; auto.
destruct h as [h1 | h1]; simpl.
rewrite h1; auto.
intros a i; split; intro h2; simpl.
inversion_clear 0; auto; inversion_clear H0; auto.
inversion_clear h2; auto; inversion_clear H0; auto.
Qed.

Hint Resolve ms_perm21.

Definition ms_perm := PermutationA eqA.

Definition ms_all_dec (m n:multiset) := 
  forall (a b:A), (In a m \/ In a n) -> (In b m \/ In b n) -> ms_dec a b.

Lemma ms_perm312 : forall (a1 a2 a3:A) (m:multiset), ms_dec3 a1 a2 a3 ->
  ms_eq (a1::a2::a3::m) (a3::a1::a2::m).
Proof.
intros a1 a2 a3 m [h1 [h2 h3]]; simpl.
change (ms_eq (munion (madd a1 (madd a2 (madd a3 nil))) m)
              (munion (madd a3 (madd a1 (madd a2 nil))) m)).
apply munion_compat; auto.
destruct h1 as [h1 | h1]; destruct h2 as [h2 | h2]; destruct h3 as [h3 | h3];
  simpl;
  intros a i; split; intro h4; simpl;
  inversion_clear h4; auto; inversion_clear H0; auto;
  inversion_clear H2; auto; inversion_clear H2; auto.
Qed.

Hint Resolve ms_perm312.

Lemma ms_perm321 : forall (a1 a2 a3:A) (m:multiset), ms_dec3 a1 a2 a3 ->
  ms_eq (a1::a2::a3::m) (a3::a2::a1::m).
Proof.
intros a1 a2 a3 m [h1 [h2 h3]]; simpl.
change (ms_eq (munion (madd a1 (madd a2 (madd a3 nil))) m)
              (munion (madd a3 (madd a2 (madd a1 nil))) m)).
apply munion_compat; auto.
destruct h1 as [h1 | h1]; destruct h2 as [h2 | h2]; destruct h3 as [h3 | h3];
  simpl;
  intros a i; split; intro h4; simpl;
  inversion_clear h4; auto; inversion_clear H0; auto;
  inversion_clear H2; auto; inversion_clear H2; auto.
Qed.

Hint Resolve ms_perm321.

Lemma ms_perm231 : forall (a1 a2 a3:A) (m:multiset), ms_dec3 a1 a2 a3 ->
  ms_eq (a1::a2::a3::m) (a2::a3::a1::m).
Proof.
intros a1 a2 a3 m [h1 [h2 h3]]; simpl.
change (ms_eq (munion (madd a1 (madd a2 (madd a3 nil))) m)
              (munion (madd a2 (madd a3 (madd a1 nil))) m)).
apply munion_compat; auto.
destruct h1 as [h1 | h1]; destruct h2 as [h2 | h2]; destruct h3 as [h3 | h3];
  simpl;
  intros a i; split; intro h4; simpl;
  inversion_clear h4; auto; inversion_clear H0; auto;
  inversion_clear H2; auto; inversion_clear H2; auto.
Qed.

Hint Resolve ms_perm231.

Lemma ms_perm4231 : forall (a1 a2 a3 a4:A) (m:multiset), ms_dec3 a1 a3 a4 ->
  ms_eq (a1::a2::a3::a4::m) (a4::a2::a3::a1::m).
Proof.
intros a1 a2 a3 a4 m [h1 [h2 h3]]; simpl.
change (ms_eq (munion (madd a1 (madd a2 (madd a3 (madd a4 nil)))) m)
              (munion (madd a4 (madd a2 (madd a3 (madd a1 nil)))) m)).
apply munion_compat; auto.
destruct h1 as [h1 | h1]; destruct h2 as [h2 | h2]; destruct h3 as [h3 | h3];
  simpl;
  intros a i; split; intro h4; simpl;
  inversion_clear h4; auto; inversion_clear H0; auto;
  inversion_clear H2; auto; inversion_clear H3; auto.
Qed.

Hint Resolve ms_perm4231.

Lemma ms_perm3412 : forall (a1 a2 a3 a4:A) (m:multiset), ms_dec4 a1 a2 a3 a4 ->
  ms_eq (a1::a2::a3::a4::m) (a3::a4::a1::a2::m).
Proof.
intros a1 a2 a3 a4 m [h1 [h2 [h3 [h4 [h5 h6]]]]]; simpl.
change (ms_eq (munion (madd a1 (madd a2 (madd a3 (madd a4 nil)))) m)
              (munion (madd a3 (madd a4 (madd a1 (madd a2 nil)))) m)).
apply munion_compat; auto.
destruct h5 as [h5 | h5]; try rewrite h5; auto.
apply ms_perm321; auto.
assert (h:ms_dec a4 a3).
unfold ms_dec in *. 
destruct h6 as [h6 | h6].
left; auto.
right; auto.
unfold ms_dec3,ms_dec in *; auto.
destruct h2 as [h2 | h2]; try rewrite h2; auto.
apply ms_eq_cons; auto.
destruct h4 as [h4 | h4]; destruct h6 as [h6 | h6];
 simpl;
 intros a i; split; intro h7; simpl;
 inversion_clear h7; auto; inversion_clear H0; auto;
 inversion_clear H2; auto; inversion_clear H3; auto.
destruct h1 as [h1 | h1]; destruct h3 as [h3 | h3];
 destruct h4 as [h4 | h4]; destruct h6 as [h6 | h6];
 simpl;
 intros a i; split; intro h7; simpl;
 inversion_clear h7; auto; inversion_clear H0; auto;
 inversion_clear H2; auto; inversion_clear H3; auto.
Qed.

Hint Resolve ms_perm4231.

Lemma ms_perm4312 : forall (a1 a2 a3 a4:A) (m:multiset), ms_dec4 a1 a2 a3 a4 ->
  ms_eq (a1::a2::a3::a4::m) (a4::a3::a1::a2::m).
Proof.
intros a1 a2 a3 a4 m [h1 [h2 [h3 [h4 [h5 h6]]]]]; simpl.
change (ms_eq (munion (madd a1 (madd a2 (madd a3 (madd a4 nil)))) m)
              (munion (madd a4 (madd a3 (madd a1 (madd a2 nil)))) m)).
apply munion_compat; auto.
destruct h5 as [h5 | h5]; try rewrite h5; auto.
apply ms_perm231; auto.
assert (h:ms_dec a4 a3).
unfold ms_dec in *. 
destruct h6 as [h6 | h6].
left; auto.
right; auto.
unfold ms_dec3,ms_dec in *; auto.
destruct h3 as [h3 | h3]; try rewrite h3; auto.
apply ms_eq_cons; auto.
destruct h4 as [h4 | h4]; destruct h6 as [h6 | h6];
 simpl;
 intros a i; split; intro h7; simpl;
 inversion_clear h7; auto; inversion_clear H0; auto;
 inversion_clear H2; auto; inversion_clear H3; auto.
destruct h1 as [h1 | h1]; destruct h2 as [h2 | h2];
 destruct h4 as [h4 | h4]; destruct h6 as [h6 | h6];
 simpl;
 intros a i; split; intro h7; simpl;
 inversion_clear h7; auto; inversion_clear H0; auto;
 inversion_clear H2; auto; inversion_clear H3; auto.
Qed.

Hint Resolve ms_perm4312.

End multiset.

