This module defines grouping by the domain of an association list
and the derived equivalence relation, grouped_equiv.
This equivalence allows elements with different keys to
be freely rearranged.
Require Import List.
Require Import ListSet.
Require Import Bool.
Require Import Permutation.
Require Import Morphisms.
Require Import Setoid.
Require Import EquivDec.
Require Import Equivalence.
Require Import RelationClasses.
Require Import Lia.
Require Import LibUtilsCoqLibAdd.
Require Import LibUtilsLift.
Require Import LibUtilsListAdd.
Require Import LibUtilsAssoc.
Require Import LibUtilsSublist.
Require Import LibUtilsBag.
Section groupbydomain.
Fixpoint groupby_domain {
A B} {
dec:
EqDec A eq} (
l:
list (
A*
B)):
list (
A*
list B)
:=
match l with
|
nil =>
nil
| (
a,
b)::
l' =>
let rest :=
groupby_domain l'
in
match lookup dec rest a with
|
Some bs =>
update_first dec rest a (
b::
bs)
|
None => (
a, (
b::
nil))::
rest
end
end.
Definition grouped_equiv {
A B} {
dec:
EqDec A eq} (
l1 l2 :
list (
A*
B))
:=
Permutation (
groupby_domain l1) (
groupby_domain l2).
Definition list_unnest {
A B} (
l:
list (
A*
list B)) :
list (
list (
A*
B))
:=
map (
fun x =>
map (
fun y => (
fst x,
y)) (
snd x))
l.
Global Instance grouped_equiv_equiv {
A B} {
dec:
EqDec A eq} :
Equivalence (@
grouped_equiv A B dec).
Proof.
unfold grouped_equiv.
constructor;
red;
intros.
-
reflexivity.
-
symmetry;
trivial.
-
etransitivity;
eauto.
Qed.
Lemma groupby_domain_concat_list_unest_perm {
A B} {
dec:
EqDec A eq} (
l:
list (
A*
B))
:
Permutation (
concat (
list_unnest (
groupby_domain l)))
l.
Proof.
Global Instance grouped_equiv_perm {
A B} {
dec:
EqDec A eq} :
subrelation (@
grouped_equiv A B dec) (@
Permutation (
A*
B)).
Proof.
Lemma groupby_domain_NoDup {
A B} {
dec:
EqDec A eq} (
l:
list (
A*
B)) :
NoDup (
domain (
groupby_domain l)).
Proof.
induction l;
simpl.
-
constructor.
-
destruct a.
match_option.
+
rewrite domain_update_first;
trivial.
+
simpl;
constructor;
trivial.
apply lookup_none_nin in eqq;
trivial.
Qed.
Lemma groupby_domain_nnil {
A B} {
dec:
EqDec A eq} (
l:
list (
A*
B)) :
~
In nil (
codomain (
groupby_domain l)).
Proof.
induction l;
simpl.
-
tauto.
-
destruct a.
match_option;
simpl.
+
destruct (
in_update_break dec eqq (
b::
l0))
as [
m1 [
m2 [
meqq mueqq]]].
rewrite mueqq;
simpl.
rewrite meqq in IHl.
rewrite codomain_app,
in_app_iff in IHl |- *;
simpl in *.
intuition discriminate.
+
intuition discriminate.
Qed.
Lemma groupby_domain_concat_list_unest_equiv {
A B} {
dec:
EqDec A eq} (
l:
list (
A*
B))
:
lookup_equiv (
concat (
list_unnest (
groupby_domain l)))
l.
Proof.
Lemma grouped_equiv_cons {
A B} {
dec:
EqDec A eq} {
l1 l2:
list (
A*
B)} :
grouped_equiv l1 l2 ->
forall a,
grouped_equiv (
a::
l1) (
a::
l2).
Proof.
Lemma grouped_equiv_cons_invs {
A B} {
dec:
EqDec A eq} {
a b b'} {
l1 l2:
list (
A*
B)} :
grouped_equiv ((
a,
b)::
l1) ((
a,
b')::
l2) ->
b =
b' /\
grouped_equiv l1 l2.
Proof.
Lemma concat_list_unnest_lookup_none {
A B} {
dec:
EqDec A eq} {
l1:
list (
A*
list B)} {
a} :
lookup dec l1 a =
None ->
lookup dec (
concat (
list_unnest l1))
a =
None.
Proof.
Lemma concat_list_unnest_lookup_some {
A B} {
dec:
EqDec A eq} {
l1:
list (
A*
list B)} {
a b l'} :
lookup dec l1 a =
Some (
b::
l') ->
lookup dec (
concat (
list_unnest l1))
a =
Some b.
Proof.
Lemma concat_list_unnest_lookup_equiv {
A B} {
dec:
EqDec A eq} (
l1 l2:
list (
A*
list B)) :
~
In nil (
codomain l1) ->
lookup_equiv l1 l2 ->
lookup_equiv (
concat (
list_unnest l1)) (
concat (
list_unnest l2)).
Proof.
Global Instance grouped_equiv_lookup_equiv {
A B} {
dec:
EqDec A eq} :
subrelation (@
grouped_equiv A B dec)
lookup_equiv.
Proof.
Lemma groupby_domain_equivlist {
A B} {
dec:
EqDec A eq} (
l:
list (
A*
B)):
equivlist (
domain (
groupby_domain l)) (
domain l).
Proof.
unfold equivlist.
induction l;
simpl; [
tauto | ].
destruct a;
simpl.
intros x.
match_option.
-
rewrite domain_update_first.
rewrite IHl.
apply lookup_in_domain in eqq.
rewrite IHl in eqq.
intuition;
congruence.
-
simpl;
rewrite IHl;
tauto.
Qed.
Lemma groupby_domain_update_first_some {
A B} {
dec:
EqDec A eq} {
l:
list (
A*
B)} {
v b l'} :
lookup dec (
groupby_domain l)
v =
Some (
b::
l') ->
forall d,
groupby_domain (
update_first dec l v d) =
update_first dec (
groupby_domain l)
v (
d::
l').
Proof.
Lemma grouped_equiv_update_first {
A B} {
dec:
EqDec A eq} (
l1 l2:
list (
A*
B))
v d :
grouped_equiv l1 l2 ->
grouped_equiv (
update_first dec l1 v d) (
update_first dec l2 v d).
Proof.
Lemma grouped_equiv_singleton {
A B} {
dec:
EqDec A eq}
a (
l:
list (
A*
B)) :
grouped_equiv (
a::
nil)
l ->
l =
a::
nil.
Proof.
Lemma groupby_domain_lookup_app_nin {
A B} {
dec:
EqDec A eq} (
l1 l2 l3 :
list (
A*
B))
x :
~
In x (
domain l2) ->
lookup dec (
groupby_domain (
l1++
l2++
l3))
x =
lookup dec (
groupby_domain (
l1++
l3))
x.
Proof.
induction l2;
simpl;
trivial.
intros nin.
apply notand in nin.
destruct nin.
destruct a;
simpl in *.
rewrite <- (
IHl2 H0).
clear IHl2 H0.
generalize (
l2++
l3);
clear l2 l3;
intros l2.
induction l1;
simpl in *.
-
match_option.
+
replace dec with (@
equiv_dec _ _ _ dec)
in *
by trivial.
rewrite lookup_update_neq;
tauto.
+
simpl.
match_destr;
try congruence.
-
destruct a0;
simpl in *.
destruct (
dec a0 x);
unfold equiv,
complement in *.
+
subst.
rewrite IHl1.
match_option;
simpl.
*
replace dec with (@
equiv_dec _ _ _ dec)
in *
by trivial.
{
repeat rewrite lookup_update_eq_in;
trivial.
-
apply lookup_in_domain in eqq;
trivial.
-
apply lookup_in_domain in eqq.
rewrite groupby_domain_equivlist in *.
rewrite domain_app,
in_app_iff in *;
simpl;
tauto.
}
*
match_destr.
+
match_option;
simpl.
*
replace dec with (@
equiv_dec _ _ _ dec)
in *
by trivial.
rewrite lookup_update_neq by tauto.
rewrite IHl1.
{
match_option.
-
repeat rewrite lookup_update_neq by tauto.
trivial.
-
simpl.
match_destr;
congruence.
}
*
destruct (
dec x a0);
try congruence.
rewrite IHl1.
{
match_option.
-
replace dec with (@
equiv_dec _ _ _ dec)
in *
by trivial.
rewrite lookup_update_neq by tauto.
trivial.
-
simpl.
match_destr;
congruence.
}
Qed.
End groupbydomain.