(* ========================================================================= *)
(*  Permuted lists.                                                          *)
(*                                                                           *)
(*  Author: Marco Maggesi                                                    *)
(*          University of Florence, Italy                                    *)
(*          http://www.math.unifi.it/~maggesi/                               *)
(*                                                                           *)
(*          (c) Copyright, Marco Maggesi, 2005-2007                          *)
(* ========================================================================= *)

needs "Permutation/morelist.ml";;

parse_as_infix("PERMUTED",(12,"right"));;

(* ------------------------------------------------------------------------- *)
(*  Permuted lists.                                                          *)
(* ------------------------------------------------------------------------- *)

let PERMUTED_RULES, PERMUTED_INDUCT, PERMUTED_CASES =
  new_inductive_definition
  `[] PERMUTED [] /\
   (!h t1 t2. t1 PERMUTED t2 ==> h :: t1 PERMUTED h :: t2) /\
   (!l1 l2 l3. l1 PERMUTED l2 /\ l2 PERMUTED l3 ==> l1 PERMUTED l3) /\
   (!x y t. x :: y :: t PERMUTED y :: x :: t)`;;

let PERMUTED_INDUCT_STRONG =
  derive_strong_induction(PERMUTED_RULES,PERMUTED_INDUCT);;

let PERMUTED_RFL = 
prove (`!l. l PERMUTED l`,
LIST_INDUCT_TAC THEN ASM_SIMP_TAC [PERMUTED_RULES]);;
let PERMUTED_SYM = 
prove (`!(xs:A list) l2. xs PERMUTED l2 <=> l2 PERMUTED xs`,
SUFFICE_TAC [] `!(xs:A list) l2. xs PERMUTED l2 ==> l2 PERMUTED xs` THEN MATCH_MP_TAC PERMUTED_INDUCT THEN ASM_MESON_TAC [PERMUTED_RULES]);;
let PERMUTED_TRS = 
prove (`!xs l2 l3. xs PERMUTED l2 /\ l2 PERMUTED l3 ==> xs PERMUTED l3`,
MESON_TAC [PERMUTED_RULES]);;
let PERMUTED_TRS_TAC tm : tactic = MATCH_MP_TAC PERMUTED_TRS THEN EXISTS_TAC tm THEN CONJ_TAC ;;
let PERMUTED_TAIL_IMP = 
prove (`!h t1 t2. t1 PERMUTED t2 ==> h :: t1 PERMUTED h :: t2`,
SIMP_TAC [PERMUTED_RULES]);;
let PERMUTED_MAP = 
prove (`!f l1 l2. l1 PERMUTED l2 ==> MAP f l1 PERMUTED MAP f l2`,
GEN_TAC THEN MATCH_MP_TAC PERMUTED_INDUCT THEN REWRITE_TAC [MAP; PERMUTED_RULES]);;
let PERMUTED_LENGTH = 
prove (`!l1 l2. l1 PERMUTED l2 ==> LENGTH l1 = LENGTH l2`,
MATCH_MP_TAC PERMUTED_INDUCT THEN REPEAT STRIP_TAC THEN ASM_REWRITE_TAC [LENGTH]);;
let PERMUTED_SWAP_HEAD = 
prove (`!a b l. a :: b :: l PERMUTED b :: a :: l`,
REWRITE_TAC [PERMUTED_RULES]);;
let PERMUTED_MEM = 
prove (`!(a:A) l1 l2. l1 PERMUTED l2 ==> (MEM a l1 <=> MEM a l2)`,
GEN_TAC THEN MATCH_MP_TAC PERMUTED_INDUCT THEN REWRITE_TAC [MEM] THEN REPEAT STRIP_TAC THEN ASM_REWRITE_TAC[] THEN MESON_TAC[]);;
let PERMUTED_ALL = 
prove (`!P xs ys. xs PERMUTED ys ==> (ALL P xs <=> ALL P ys)`,
GEN_TAC THEN MATCH_MP_TAC PERMUTED_INDUCT THEN REWRITE_TAC [ALL] THEN REPEAT STRIP_TAC THEN ASM_SIMP_TAC[] THEN MESON_TAC[]);;
let PERMUTED_NIL_EQ_NIL = 
prove (`(!l:A list. [] PERMUTED l <=> l = []) /\ (!l:A list. l PERMUTED [] <=> l = [])`,
SUFFICE_TAC [PERMUTED_SYM] `!l:A list. [] PERMUTED l <=> l = []` THEN LIST_CASES_TAC THEN ASM_REWRITE_TAC [NOT_CONS_NIL; PERMUTED_RFL] THEN MESON_TAC [PERMUTED_LENGTH; LENGTH; NOT_SUC]);;
let PERMUTED_SINGLETON = 
prove (`(!(x:A) l. [x] PERMUTED l <=> l = [x]) /\ (!(x:A) l. l PERMUTED [x] <=> l = [x])`,
SUFFICE_TAC [PERMUTED_LENGTH; PERMUTED_RFL] `!l1 l2. l1 PERMUTED l2 ==> LENGTH l1 = LENGTH l2 /\ (!x. l1 = [x:A] <=> l2 = [x])` THEN MATCH_MP_TAC PERMUTED_INDUCT THEN SIMP_TAC [PERMUTED_NIL_EQ_NIL; LENGTH; NOT_CONS_NIL; CONS_11; SUC_INJ; GSYM LENGTH_EQ_NIL]);;
let PERMUTED_CONS_DELETE1 = 
prove (`!(a:A) l. MEM a l ==> l PERMUTED a :: DELETE1 a l`,
GEN_TAC THEN LIST_INDUCT_TAC THEN REWRITE_TAC [MEM; DELETE1] THEN COND_CASES_TAC THEN ASM_MESON_TAC [PERMUTED_RFL; PERMUTED_TAIL_IMP; PERMUTED_SWAP_HEAD; PERMUTED_TRS]);;
let PERMUTED_COUNT = 
prove (`!l1 l2. l1 PERMUTED l2 <=> (!x:A. COUNT x l1 = COUNT x l2)`,
let IFF_EXPAND = MESON [] `(p <=> q) <=> (p ==> q) /\ (q ==> p)` in REWRITE_TAC [IFF_EXPAND; FORALL_AND_THM] THEN CONJ_TAC THENL [MATCH_MP_TAC PERMUTED_INDUCT THEN REWRITE_TAC [COUNT] THEN ASM_MESON_TAC []; ALL_TAC] THEN LIST_INDUCT_TAC THEN REWRITE_TAC [COUNT; PERMUTED_NIL_EQ_NIL] THENL [LIST_CASES_TAC THEN REWRITE_TAC [COUNT; NOT_CONS_NIL] THEN MESON_TAC [NOT_SUC]; ALL_TAC] THEN REPEAT STRIP_TAC THEN ASSERT_TAC `MEM (h:A) l2` THENL [FIRST_X_ASSUM (MP_TAC o SPEC `h:A`) THEN REWRITE_TAC[MEM_COUNT] THEN ARITH_TAC; ALL_TAC] THEN ASSERT_TAC `(h:A) :: t PERMUTED h :: DELETE1 h l2` THENL [MATCH_MP_TAC PERMUTED_TAIL_IMP THEN FIRST_X_ASSUM MATCH_MP_TAC THEN ASM_REWRITE_TAC [COUNT_DELETE1] THEN GEN_TAC THEN FIRST_X_ASSUM (MP_TAC o SPEC `x:A`) THEN ARITH_TAC; ASM_MESON_TAC [PERMUTED_CONS_DELETE1; PERMUTED_SYM; PERMUTED_TRS]]);;
let PERMUTED_TAIL = 
prove (`!x t1 t2. x :: t1 PERMUTED x :: t2 <=> t1 PERMUTED t2`,
REPEAT GEN_TAC THEN REWRITE_TAC [PERMUTED_COUNT; COUNT] THEN MESON_TAC [SUC_INJ]);;
let PERMUTED_DELETE1_L = 
prove (`!(h:A) t l. h :: t PERMUTED l <=> MEM h l /\ t PERMUTED DELETE1 h l`,
let PERMUTED_DELETE1_R = 
prove (`!(h:A) t l. l PERMUTED h :: t <=> MEM h l /\ DELETE1 h l PERMUTED t`,
let PERMUTED_LIST_UNIQ = 
prove (`!xs ys. xs PERMUTED ys ==> (LIST_UNIQ xs <=> LIST_UNIQ ys)`,
let PERMUTED_IMP_PAIRWISE = 
prove (`!(P:A->A->bool) l l'. (!x y. P x y ==> P y x) /\ l PERMUTED l' /\ PAIRWISE P l ==> PAIRWISE P l'`,
REWRITE_TAC[IMP_CONJ; RIGHT_FORALL_IMP_THM] THEN GEN_TAC THEN DISCH_TAC THEN MATCH_MP_TAC PERMUTED_INDUCT_STRONG THEN ASM_SIMP_TAC[PAIRWISE; ALL] THEN MESON_TAC[PERMUTED_ALL]);;
let PERMUTED_PAIRWISE = 
prove (`!(P:A->A->bool) l l. (!x y. P x y ==> P y x) /\ l PERMUTED l' ==> (PAIRWISE P l <=> PAIRWISE P l')`,
let PERMUTED_APPEND_SWAP = 
prove (`!l1 l2. (APPEND l1 l2) PERMUTED (APPEND l2 l1)`,
REWRITE_TAC[PERMUTED_COUNT; COUNT_APPEND] THEN ARITH_TAC);;