# Theory Enum

Up to index of Isabelle/HOL

theory Enum
imports Map
`(* Author: Florian Haftmann, TU Muenchen *)header {* Finite types as explicit enumerations *}theory Enumimports Mapbeginsubsection {* Class @{text enum} *}class enum =  fixes enum :: "'a list"  fixes enum_all :: "('a => bool) => bool"  fixes enum_ex :: "('a => bool) => bool"  assumes UNIV_enum: "UNIV = set enum"    and enum_distinct: "distinct enum"  assumes enum_all_UNIV: "enum_all P <-> Ball UNIV P"  assumes enum_ex_UNIV: "enum_ex P <-> Bex UNIV P"    -- {* tailored towards simple instantiation *}beginsubclass finite proofqed (simp add: UNIV_enum)lemma enum_UNIV:  "set enum = UNIV"  by (simp only: UNIV_enum)lemma in_enum: "x ∈ set enum"  by (simp add: enum_UNIV)lemma enum_eq_I:  assumes "!!x. x ∈ set xs"  shows "set enum = set xs"proof -  from assms UNIV_eq_I have "UNIV = set xs" by auto  with enum_UNIV show ?thesis by simpqedlemma card_UNIV_length_enum:  "card (UNIV :: 'a set) = length enum"  by (simp add: UNIV_enum distinct_card enum_distinct)lemma enum_all [simp]:  "enum_all = HOL.All"  by (simp add: fun_eq_iff enum_all_UNIV)lemma enum_ex [simp]:  "enum_ex = HOL.Ex"   by (simp add: fun_eq_iff enum_ex_UNIV)endsubsection {* Implementations using @{class enum} *}subsubsection {* Unbounded operations and quantifiers *}lemma Collect_code [code]:  "Collect P = set (filter P enum)"  by (simp add: enum_UNIV)lemma vimage_code [code]:  "f -` B = set (filter (%x. f x : B) enum_class.enum)"  unfolding vimage_def Collect_code ..definition card_UNIV :: "'a itself => nat"where  [code del]: "card_UNIV TYPE('a) = card (UNIV :: 'a set)"lemma [code]:  "card_UNIV TYPE('a :: enum) = card (set (Enum.enum :: 'a list))"  by (simp only: card_UNIV_def enum_UNIV)lemma all_code [code]: "(∀x. P x) <-> enum_all P"  by simplemma exists_code [code]: "(∃x. P x) <-> enum_ex P"  by simplemma exists1_code [code]: "(∃!x. P x) <-> list_ex1 P enum"  by (auto simp add: list_ex1_iff enum_UNIV)subsubsection {* An executable choice operator *}definition  [code del]: "enum_the = The"lemma [code]:  "The P = (case filter P enum of [x] => x | _ => enum_the P)"proof -  {    fix a    assume filter_enum: "filter P enum = [a]"    have "The P = a"    proof (rule the_equality)      fix x      assume "P x"      show "x = a"      proof (rule ccontr)        assume "x ≠ a"        from filter_enum obtain us vs          where enum_eq: "enum = us @ [a] @ vs"          and "∀ x ∈ set us. ¬ P x"          and "∀ x ∈ set vs. ¬ P x"          and "P a"          by (auto simp add: filter_eq_Cons_iff) (simp only: filter_empty_conv[symmetric])        with `P x` in_enum[of x, unfolded enum_eq] `x ≠ a` show "False" by auto      qed    next      from filter_enum show "P a" by (auto simp add: filter_eq_Cons_iff)    qed  }  from this show ?thesis    unfolding enum_the_def by (auto split: list.split)qedcode_abort enum_thecode_const enum_the (Eval "(fn p => raise Match)")subsubsection {* Equality and order on functions *}instantiation "fun" :: (enum, equal) equalbegindefinition  "HOL.equal f g <-> (∀x ∈ set enum. f x = g x)"instance proofqed (simp_all add: equal_fun_def fun_eq_iff enum_UNIV)endlemma [code]:  "HOL.equal f g <-> enum_all (%x. f x = g x)"  by (auto simp add: equal fun_eq_iff)lemma [code nbe]:  "HOL.equal (f :: _ => _) f <-> True"  by (fact equal_refl)lemma order_fun [code]:  fixes f g :: "'a::enum => 'b::order"  shows "f ≤ g <-> enum_all (λx. f x ≤ g x)"    and "f < g <-> f ≤ g ∧ enum_ex (λx. f x ≠ g x)"  by (simp_all add: fun_eq_iff le_fun_def order_less_le)subsubsection {* Operations on relations *}lemma [code]:  "Id = image (λx. (x, x)) (set Enum.enum)"  by (auto intro: imageI in_enum)lemma tranclp_unfold [code, no_atp]:  "tranclp r a b <-> (a, b) ∈ trancl {(x, y). r x y}"  by (simp add: trancl_def)lemma rtranclp_rtrancl_eq [code, no_atp]:  "rtranclp r x y <-> (x, y) ∈ rtrancl {(x, y). r x y}"  by (simp add: rtrancl_def)lemma max_ext_eq [code]:  "max_ext R = {(X, Y). finite X ∧ finite Y ∧ Y ≠ {} ∧ (∀x. x ∈ X --> (∃xa ∈ Y. (x, xa) ∈ R))}"  by (auto simp add: max_ext.simps)lemma max_extp_eq [code]:  "max_extp r x y <-> (x, y) ∈ max_ext {(x, y). r x y}"  by (simp add: max_ext_def)lemma mlex_eq [code]:  "f <*mlex*> R = {(x, y). f x < f y ∨ (f x ≤ f y ∧ (x, y) ∈ R)}"  by (auto simp add: mlex_prod_def)lemma [code]:  fixes xs :: "('a::finite × 'a) list"  shows "acc (set xs) = bacc (set xs) (card_UNIV TYPE('a))"  by (simp add: card_UNIV_def acc_bacc_eq)lemma [code]:  "accp r = (λx. x ∈ acc {(x, y). r x y})"  by (simp add: acc_def)subsection {* Default instances for @{class enum} *}lemma map_of_zip_enum_is_Some:  assumes "length ys = length (enum :: 'a::enum list)"  shows "∃y. map_of (zip (enum :: 'a::enum list) ys) x = Some y"proof -  from assms have "x ∈ set (enum :: 'a::enum list) <->    (∃y. map_of (zip (enum :: 'a::enum list) ys) x = Some y)"    by (auto intro!: map_of_zip_is_Some)  then show ?thesis using enum_UNIV by autoqedlemma map_of_zip_enum_inject:  fixes xs ys :: "'b::enum list"  assumes length: "length xs = length (enum :: 'a::enum list)"      "length ys = length (enum :: 'a::enum list)"    and map_of: "the o map_of (zip (enum :: 'a::enum list) xs) = the o map_of (zip (enum :: 'a::enum list) ys)"  shows "xs = ys"proof -  have "map_of (zip (enum :: 'a list) xs) = map_of (zip (enum :: 'a list) ys)"  proof    fix x :: 'a    from length map_of_zip_enum_is_Some obtain y1 y2      where "map_of (zip (enum :: 'a list) xs) x = Some y1"        and "map_of (zip (enum :: 'a list) ys) x = Some y2" by blast    moreover from map_of      have "the (map_of (zip (enum :: 'a::enum list) xs) x) = the (map_of (zip (enum :: 'a::enum list) ys) x)"      by (auto dest: fun_cong)    ultimately show "map_of (zip (enum :: 'a::enum list) xs) x = map_of (zip (enum :: 'a::enum list) ys) x"      by simp  qed  with length enum_distinct show "xs = ys" by (rule map_of_zip_inject)qeddefinition all_n_lists :: "(('a :: enum) list => bool) => nat => bool"where  "all_n_lists P n <-> (∀xs ∈ set (List.n_lists n enum). P xs)"lemma [code]:  "all_n_lists P n <-> (if n = 0 then P [] else enum_all (%x. all_n_lists (%xs. P (x # xs)) (n - 1)))"  unfolding all_n_lists_def enum_all  by (cases n) (auto simp add: enum_UNIV)definition ex_n_lists :: "(('a :: enum) list => bool) => nat => bool"where  "ex_n_lists P n <-> (∃xs ∈ set (List.n_lists n enum). P xs)"lemma [code]:  "ex_n_lists P n <-> (if n = 0 then P [] else enum_ex (%x. ex_n_lists (%xs. P (x # xs)) (n - 1)))"  unfolding ex_n_lists_def enum_ex  by (cases n) (auto simp add: enum_UNIV)instantiation "fun" :: (enum, enum) enumbegindefinition  "enum = map (λys. the o map_of (zip (enum::'a list) ys)) (List.n_lists (length (enum::'a::enum list)) enum)"definition  "enum_all P = all_n_lists (λbs. P (the o map_of (zip enum bs))) (length (enum :: 'a list))"definition  "enum_ex P = ex_n_lists (λbs. P (the o map_of (zip enum bs))) (length (enum :: 'a list))"instance proof  show "UNIV = set (enum :: ('a => 'b) list)"  proof (rule UNIV_eq_I)    fix f :: "'a => 'b"    have "f = the o map_of (zip (enum :: 'a::enum list) (map f enum))"      by (auto simp add: map_of_zip_map fun_eq_iff intro: in_enum)    then show "f ∈ set enum"      by (auto simp add: enum_fun_def set_n_lists intro: in_enum)  qednext  from map_of_zip_enum_inject  show "distinct (enum :: ('a => 'b) list)"    by (auto intro!: inj_onI simp add: enum_fun_def      distinct_map distinct_n_lists enum_distinct set_n_lists)next  fix P  show "enum_all (P :: ('a => 'b) => bool) = Ball UNIV P"  proof    assume "enum_all P"    show "Ball UNIV P"    proof      fix f :: "'a => 'b"      have f: "f = the o map_of (zip (enum :: 'a::enum list) (map f enum))"        by (auto simp add: map_of_zip_map fun_eq_iff intro: in_enum)      from `enum_all P` have "P (the o map_of (zip enum (map f enum)))"        unfolding enum_all_fun_def all_n_lists_def        apply (simp add: set_n_lists)        apply (erule_tac x="map f enum" in allE)        apply (auto intro!: in_enum)        done      from this f show "P f" by auto    qed  next    assume "Ball UNIV P"    from this show "enum_all P"      unfolding enum_all_fun_def all_n_lists_def by auto  qednext  fix P  show "enum_ex (P :: ('a => 'b) => bool) = Bex UNIV P"  proof    assume "enum_ex P"    from this show "Bex UNIV P"      unfolding enum_ex_fun_def ex_n_lists_def by auto  next    assume "Bex UNIV P"    from this obtain f where "P f" ..    have f: "f = the o map_of (zip (enum :: 'a::enum list) (map f enum))"      by (auto simp add: map_of_zip_map fun_eq_iff intro: in_enum)     from `P f` this have "P (the o map_of (zip (enum :: 'a::enum list) (map f enum)))"      by auto    from  this show "enum_ex P"      unfolding enum_ex_fun_def ex_n_lists_def      apply (auto simp add: set_n_lists)      apply (rule_tac x="map f enum" in exI)      apply (auto intro!: in_enum)      done  qedqedendlemma enum_fun_code [code]: "enum = (let enum_a = (enum :: 'a::{enum, equal} list)  in map (λys. the o map_of (zip enum_a ys)) (List.n_lists (length enum_a) enum))"  by (simp add: enum_fun_def Let_def)lemma enum_all_fun_code [code]:  "enum_all P = (let enum_a = (enum :: 'a::{enum, equal} list)   in all_n_lists (λbs. P (the o map_of (zip enum_a bs))) (length enum_a))"  by (simp only: enum_all_fun_def Let_def)lemma enum_ex_fun_code [code]:  "enum_ex P = (let enum_a = (enum :: 'a::{enum, equal} list)   in ex_n_lists (λbs. P (the o map_of (zip enum_a bs))) (length enum_a))"  by (simp only: enum_ex_fun_def Let_def)instantiation set :: (enum) enumbegindefinition  "enum = map set (sublists enum)"definition  "enum_all P <-> (∀A∈set enum. P (A::'a set))"definition  "enum_ex P <-> (∃A∈set enum. P (A::'a set))"instance proofqed (simp_all add: enum_set_def enum_all_set_def enum_ex_set_def sublists_powset distinct_set_sublists  enum_distinct enum_UNIV)endinstantiation unit :: enumbegindefinition  "enum = [()]"definition  "enum_all P = P ()"definition  "enum_ex P = P ()"instance proofqed (auto simp add: enum_unit_def enum_all_unit_def enum_ex_unit_def)endinstantiation bool :: enumbegindefinition  "enum = [False, True]"definition  "enum_all P <-> P False ∧ P True"definition  "enum_ex P <-> P False ∨ P True"instance proofqed (simp_all only: enum_bool_def enum_all_bool_def enum_ex_bool_def UNIV_bool, simp_all)endinstantiation prod :: (enum, enum) enumbegindefinition  "enum = List.product enum enum"definition  "enum_all P = enum_all (%x. enum_all (%y. P (x, y)))"definition  "enum_ex P = enum_ex (%x. enum_ex (%y. P (x, y)))" instance by default  (simp_all add: enum_prod_def product_list_set distinct_product    enum_UNIV enum_distinct enum_all_prod_def enum_ex_prod_def)endinstantiation sum :: (enum, enum) enumbegindefinition  "enum = map Inl enum @ map Inr enum"definition  "enum_all P <-> enum_all (λx. P (Inl x)) ∧ enum_all (λx. P (Inr x))"definition  "enum_ex P <-> enum_ex (λx. P (Inl x)) ∨ enum_ex (λx. P (Inr x))"instance proofqed (simp_all only: enum_sum_def enum_all_sum_def enum_ex_sum_def UNIV_sum,  auto simp add: enum_UNIV distinct_map enum_distinct)endinstantiation option :: (enum) enumbegindefinition  "enum = None # map Some enum"definition  "enum_all P <-> P None ∧ enum_all (λx. P (Some x))"definition  "enum_ex P <-> P None ∨ enum_ex (λx. P (Some x))"instance proofqed (simp_all only: enum_option_def enum_all_option_def enum_ex_option_def UNIV_option_conv,  auto simp add: distinct_map enum_UNIV enum_distinct)endsubsection {* Small finite types *}text {* We define small finite types for the use in Quickcheck *}datatype finite_1 = a⇣1notation (output) a⇣1  ("a⇣1")lemma UNIV_finite_1:  "UNIV = {a⇣1}"  by (auto intro: finite_1.exhaust)instantiation finite_1 :: enumbegindefinition  "enum = [a⇣1]"definition  "enum_all P = P a⇣1"definition  "enum_ex P = P a⇣1"instance proofqed (simp_all only: enum_finite_1_def enum_all_finite_1_def enum_ex_finite_1_def UNIV_finite_1, simp_all)endinstantiation finite_1 :: linorderbegindefinition less_finite_1 :: "finite_1 => finite_1 => bool"where  "x < (y :: finite_1) <-> False"definition less_eq_finite_1 :: "finite_1 => finite_1 => bool"where  "x ≤ (y :: finite_1) <-> True"instanceapply (intro_classes)apply (auto simp add: less_finite_1_def less_eq_finite_1_def)apply (metis finite_1.exhaust)doneendhide_const (open) a⇣1datatype finite_2 = a⇣1 | a⇣2notation (output) a⇣1  ("a⇣1")notation (output) a⇣2  ("a⇣2")lemma UNIV_finite_2:  "UNIV = {a⇣1, a⇣2}"  by (auto intro: finite_2.exhaust)instantiation finite_2 :: enumbegindefinition  "enum = [a⇣1, a⇣2]"definition  "enum_all P <-> P a⇣1 ∧ P a⇣2"definition  "enum_ex P <-> P a⇣1 ∨ P a⇣2"instance proofqed (simp_all only: enum_finite_2_def enum_all_finite_2_def enum_ex_finite_2_def UNIV_finite_2, simp_all)endinstantiation finite_2 :: linorderbegindefinition less_finite_2 :: "finite_2 => finite_2 => bool"where  "x < y <-> x = a⇣1 ∧ y = a⇣2"definition less_eq_finite_2 :: "finite_2 => finite_2 => bool"where  "x ≤ y <-> x = y ∨ x < (y :: finite_2)"instanceapply (intro_classes)apply (auto simp add: less_finite_2_def less_eq_finite_2_def)apply (metis finite_2.nchotomy)+doneendhide_const (open) a⇣1 a⇣2datatype finite_3 = a⇣1 | a⇣2 | a⇣3notation (output) a⇣1  ("a⇣1")notation (output) a⇣2  ("a⇣2")notation (output) a⇣3  ("a⇣3")lemma UNIV_finite_3:  "UNIV = {a⇣1, a⇣2, a⇣3}"  by (auto intro: finite_3.exhaust)instantiation finite_3 :: enumbegindefinition  "enum = [a⇣1, a⇣2, a⇣3]"definition  "enum_all P <-> P a⇣1 ∧ P a⇣2 ∧ P a⇣3"definition  "enum_ex P <-> P a⇣1 ∨ P a⇣2 ∨ P a⇣3"instance proofqed (simp_all only: enum_finite_3_def enum_all_finite_3_def enum_ex_finite_3_def UNIV_finite_3, simp_all)endinstantiation finite_3 :: linorderbegindefinition less_finite_3 :: "finite_3 => finite_3 => bool"where  "x < y = (case x of a⇣1 => y ≠ a⇣1 | a⇣2 => y = a⇣3 | a⇣3 => False)"definition less_eq_finite_3 :: "finite_3 => finite_3 => bool"where  "x ≤ y <-> x = y ∨ x < (y :: finite_3)"instance proof (intro_classes)qed (auto simp add: less_finite_3_def less_eq_finite_3_def split: finite_3.split_asm)endhide_const (open) a⇣1 a⇣2 a⇣3datatype finite_4 = a⇣1 | a⇣2 | a⇣3 | a⇣4notation (output) a⇣1  ("a⇣1")notation (output) a⇣2  ("a⇣2")notation (output) a⇣3  ("a⇣3")notation (output) a⇣4  ("a⇣4")lemma UNIV_finite_4:  "UNIV = {a⇣1, a⇣2, a⇣3, a⇣4}"  by (auto intro: finite_4.exhaust)instantiation finite_4 :: enumbegindefinition  "enum = [a⇣1, a⇣2, a⇣3, a⇣4]"definition  "enum_all P <-> P a⇣1 ∧ P a⇣2 ∧ P a⇣3 ∧ P a⇣4"definition  "enum_ex P <-> P a⇣1 ∨ P a⇣2 ∨ P a⇣3 ∨ P a⇣4"instance proofqed (simp_all only: enum_finite_4_def enum_all_finite_4_def enum_ex_finite_4_def UNIV_finite_4, simp_all)endhide_const (open) a⇣1 a⇣2 a⇣3 a⇣4datatype finite_5 = a⇣1 | a⇣2 | a⇣3 | a⇣4 | a⇣5notation (output) a⇣1  ("a⇣1")notation (output) a⇣2  ("a⇣2")notation (output) a⇣3  ("a⇣3")notation (output) a⇣4  ("a⇣4")notation (output) a⇣5  ("a⇣5")lemma UNIV_finite_5:  "UNIV = {a⇣1, a⇣2, a⇣3, a⇣4, a⇣5}"  by (auto intro: finite_5.exhaust)instantiation finite_5 :: enumbegindefinition  "enum = [a⇣1, a⇣2, a⇣3, a⇣4, a⇣5]"definition  "enum_all P <-> P a⇣1 ∧ P a⇣2 ∧ P a⇣3 ∧ P a⇣4 ∧ P a⇣5"definition  "enum_ex P <-> P a⇣1 ∨ P a⇣2 ∨ P a⇣3 ∨ P a⇣4 ∨ P a⇣5"instance proofqed (simp_all only: enum_finite_5_def enum_all_finite_5_def enum_ex_finite_5_def UNIV_finite_5, simp_all)endhide_const (open) a⇣1 a⇣2 a⇣3 a⇣4 a⇣5subsection {* Closing up *}hide_type (open) finite_1 finite_2 finite_3 finite_4 finite_5hide_const (open) enum enum_all enum_ex all_n_lists ex_n_lists ntranclend`