Theory Enum

Up to index of Isabelle/HOL

theory Enum
imports Map
(* Author: Florian Haftmann, TU Muenchen *)

header {* Finite types as explicit enumerations *}

theory Enum
imports Map
begin

subsection {* 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 *}
begin

subclass finite proof
qed (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 simp
qed

lemma 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)

end


subsection {* 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 simp

lemma exists_code [code]: "(∃x. P x) <-> enum_ex P"
by simp

lemma 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)
qed

code_abort enum_the
code_const enum_the (Eval "(fn p => raise Match)")


subsubsection {* Equality and order on functions *}

instantiation "fun" :: (enum, equal) equal
begin

definition
"HOL.equal f g <-> (∀x ∈ set enum. f x = g x)"

instance proof
qed (simp_all add: equal_fun_def fun_eq_iff enum_UNIV)

end

lemma [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 auto
qed

lemma 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)
qed

definition 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) enum
begin

definition
"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)
qed
next
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
qed
next
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
qed
qed

end

lemma 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) enum
begin

definition
"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 proof
qed (simp_all add: enum_set_def enum_all_set_def enum_ex_set_def sublists_powset distinct_set_sublists
enum_distinct enum_UNIV)

end

instantiation unit :: enum
begin

definition
"enum = [()]"

definition
"enum_all P = P ()"

definition
"enum_ex P = P ()"

instance proof
qed (auto simp add: enum_unit_def enum_all_unit_def enum_ex_unit_def)

end

instantiation bool :: enum
begin

definition
"enum = [False, True]"

definition
"enum_all P <-> P False ∧ P True"

definition
"enum_ex P <-> P False ∨ P True"

instance proof
qed (simp_all only: enum_bool_def enum_all_bool_def enum_ex_bool_def UNIV_bool, simp_all)

end

instantiation prod :: (enum, enum) enum
begin

definition
"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)

end

instantiation sum :: (enum, enum) enum
begin

definition
"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 proof
qed (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)

end

instantiation option :: (enum) enum
begin

definition
"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 proof
qed (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)

end


subsection {* Small finite types *}

text {* We define small finite types for the use in Quickcheck *}

datatype finite_1 = a1

notation (output) a1 ("a1")

lemma UNIV_finite_1:
"UNIV = {a1}"
by (auto intro: finite_1.exhaust)

instantiation finite_1 :: enum
begin

definition
"enum = [a1]"

definition
"enum_all P = P a1"

definition
"enum_ex P = P a1"

instance proof
qed (simp_all only: enum_finite_1_def enum_all_finite_1_def enum_ex_finite_1_def UNIV_finite_1, simp_all)

end

instantiation finite_1 :: linorder
begin

definition 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"

instance
apply (intro_classes)
apply (auto simp add: less_finite_1_def less_eq_finite_1_def)
apply (metis finite_1.exhaust)
done

end

hide_const (open) a1

datatype finite_2 = a1 | a2

notation (output) a1 ("a1")
notation (output) a2 ("a2")

lemma UNIV_finite_2:
"UNIV = {a1, a2}"
by (auto intro: finite_2.exhaust)

instantiation finite_2 :: enum
begin

definition
"enum = [a1, a2]"

definition
"enum_all P <-> P a1 ∧ P a2"

definition
"enum_ex P <-> P a1 ∨ P a2"

instance proof
qed (simp_all only: enum_finite_2_def enum_all_finite_2_def enum_ex_finite_2_def UNIV_finite_2, simp_all)

end

instantiation finite_2 :: linorder
begin

definition less_finite_2 :: "finite_2 => finite_2 => bool"
where
"x < y <-> x = a1 ∧ y = a2"

definition less_eq_finite_2 :: "finite_2 => finite_2 => bool"
where
"x ≤ y <-> x = y ∨ x < (y :: finite_2)"

instance
apply (intro_classes)
apply (auto simp add: less_finite_2_def less_eq_finite_2_def)
apply (metis finite_2.nchotomy)+
done

end

hide_const (open) a1 a2

datatype finite_3 = a1 | a2 | a3

notation (output) a1 ("a1")
notation (output) a2 ("a2")
notation (output) a3 ("a3")

lemma UNIV_finite_3:
"UNIV = {a1, a2, a3}"
by (auto intro: finite_3.exhaust)

instantiation finite_3 :: enum
begin

definition
"enum = [a1, a2, a3]"

definition
"enum_all P <-> P a1 ∧ P a2 ∧ P a3"

definition
"enum_ex P <-> P a1 ∨ P a2 ∨ P a3"

instance proof
qed (simp_all only: enum_finite_3_def enum_all_finite_3_def enum_ex_finite_3_def UNIV_finite_3, simp_all)

end

instantiation finite_3 :: linorder
begin

definition less_finite_3 :: "finite_3 => finite_3 => bool"
where
"x < y = (case x of a1 => y ≠ a1 | a2 => y = a3 | a3 => 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)

end

hide_const (open) a1 a2 a3

datatype finite_4 = a1 | a2 | a3 | a4

notation (output) a1 ("a1")
notation (output) a2 ("a2")
notation (output) a3 ("a3")
notation (output) a4 ("a4")

lemma UNIV_finite_4:
"UNIV = {a1, a2, a3, a4}"
by (auto intro: finite_4.exhaust)

instantiation finite_4 :: enum
begin

definition
"enum = [a1, a2, a3, a4]"

definition
"enum_all P <-> P a1 ∧ P a2 ∧ P a3 ∧ P a4"

definition
"enum_ex P <-> P a1 ∨ P a2 ∨ P a3 ∨ P a4"

instance proof
qed (simp_all only: enum_finite_4_def enum_all_finite_4_def enum_ex_finite_4_def UNIV_finite_4, simp_all)

end

hide_const (open) a1 a2 a3 a4


datatype finite_5 = a1 | a2 | a3 | a4 | a5

notation (output) a1 ("a1")
notation (output) a2 ("a2")
notation (output) a3 ("a3")
notation (output) a4 ("a4")
notation (output) a5 ("a5")

lemma UNIV_finite_5:
"UNIV = {a1, a2, a3, a4, a5}"
by (auto intro: finite_5.exhaust)

instantiation finite_5 :: enum
begin

definition
"enum = [a1, a2, a3, a4, a5]"

definition
"enum_all P <-> P a1 ∧ P a2 ∧ P a3 ∧ P a4 ∧ P a5"

definition
"enum_ex P <-> P a1 ∨ P a2 ∨ P a3 ∨ P a4 ∨ P a5"

instance proof
qed (simp_all only: enum_finite_5_def enum_all_finite_5_def enum_ex_finite_5_def UNIV_finite_5, simp_all)

end

hide_const (open) a1 a2 a3 a4 a5


subsection {* Closing up *}

hide_type (open) finite_1 finite_2 finite_3 finite_4 finite_5
hide_const (open) enum enum_all enum_ex all_n_lists ex_n_lists ntrancl

end