Session HOLCF-Prelude

Theory HOLCF_Main

section ‹Initial Setup for HOLCF-Prelude›

theory HOLCF_Main
  imports
    HOLCF
    "HOLCF-Library.Int_Discrete"
    "HOL-Library.Adhoc_Overloading"
begin

text ‹
  All theories from the Isabelle distribution which are used
  anywhere in the HOLCF-Prelude library must be imported via this file.
  This way, we only have to hide constant names and syntax in one place.
›

hide_type (open) list

hide_const (open)
  List.append List.concat List.Cons List.distinct List.filter List.last
  List.foldr List.foldl List.length List.lists List.map List.Nil List.nth
  List.partition List.replicate List.set List.take List.upto List.zip
  Orderings.less Product_Type.fst Product_Type.snd

no_notation Map.map_add (infixl "++" 100)

no_notation List.upto ("(1[_../_])")

no_notation
  Rings.divide (infixl "div" 70) and
  Rings.modulo (infixl "mod" 70)

no_notation
  Set.member  ("(:)") and
  Set.member  ("(_/ : _)" [51, 51] 50)

no_translations
  "[x, xs]" == "x # [xs]"
  "[x]" == "x # []"

no_syntax
  "_list" :: "args  'a List.list"    ("[(_)]")

no_notation
  List.Nil ("[]")

no_syntax "_bracket" :: "types  type  type" ("([_]/ => _)" [0, 0] 0)
no_syntax "_bracket" :: "types  type  type" ("([_]/  _)" [0, 0] 0)

no_translations
  "[x<-xs . P]" == "CONST List.filter (%x. P) xs"

no_syntax (ASCII)
  "_filter" :: "pttrn  'a List.list  bool  'a List.list" ("(1[_<-_./ _])")

no_syntax
  "_filter" :: "pttrn  'a List.list  bool  'a List.list" ("(1[__ ./ _])")

text ‹Declarations that belong in HOLCF/Tr.thy:›

declare trE [cases type: tr]
declare tr_induct [induct type: tr]

end

Theory Type_Classes

section ‹Type Classes›

theory Type_Classes
  imports HOLCF_Main
begin


subsection ‹Eq class›

class Eq =
  fixes eq :: "'a  'a  tr"

text ‹
  The Haskell type class does allow /= to be specified separately. For now, we assume
  that all modeled type classes use the default implementation, or an equivalent.
›
fixrec neq :: "'a::Eq  'a  tr" where
  "neqxy = neg(eqxy)"

class Eq_strict = Eq +
  assumes eq_strict [simp]:
    "eqx = "
    "eqy = "

class Eq_sym = Eq_strict +
  assumes eq_sym: "eqxy = eqyx"

class Eq_equiv = Eq_sym +
  assumes eq_self_neq_FF [simp]: "eqxx  FF"
    and eq_trans: "eqxy = TT  eqyz = TT  eqxz = TT"
begin

lemma eq_refl: "eqxx    eqxx = TT"
  by (cases "eqxx") simp+

end

class Eq_eq = Eq_sym +
  assumes eq_self_neq_FF': "eqxx  FF"
    and eq_TT_dest: "eqxy = TT  x = y"
begin

subclass Eq_equiv
  by standard (auto simp: eq_self_neq_FF' dest: eq_TT_dest)

lemma eqD [dest]:
  "eqxy = TT  x = y"
  "eqxy = FF  x  y"
  by (auto elim: eq_TT_dest)

end

subsubsection ‹Class instances›

instantiation lift :: (countable) Eq_eq
begin

definition "eq  (Λ(Def x) (Def y). Def (x = y))"

instance
  by standard (auto simp: eq_lift_def flift1_def split: lift.splits)

end

lemma eq_ONE_ONE [simp]: "eqONEONE = TT"
  unfolding ONE_def eq_lift_def by simp


subsection ‹Ord class›

domain Ordering = LT | EQ | GT

definition oppOrdering :: "Ordering  Ordering" where
  "oppOrdering = (Λ x. case x of LT  GT | EQ  EQ | GT  LT)"

lemma oppOrdering_simps [simp]:
  "oppOrderingLT = GT"
  "oppOrderingEQ = EQ"
  "oppOrderingGT = LT"
  "oppOrdering = "
  unfolding oppOrdering_def by simp_all

class Ord = Eq +
  fixes compare :: "'a  'a  Ordering"
begin

definition lt :: "'a  'a  tr" where
  "lt = (Λ x y. case comparexy of LT  TT | EQ  FF | GT  FF)"

definition le :: "'a  'a  tr" where
  "le = (Λ x y. case comparexy of LT  TT | EQ  TT | GT  FF)"

lemma lt_eq_TT_iff: "ltxy = TT  comparexy = LT"
  by (cases "comparexy") (simp add: lt_def)+

end

class Ord_strict = Ord +
  assumes compare_strict [simp]:
    "comparey = "
    "comparex = "
begin

lemma lt_strict [simp]:
  shows "ltx = "
    and "ltx = "
  by (simp add: lt_def)+

lemma le_strict [simp]:
  shows "lex = "
    and "lex = "
  by (simp add: le_def)+

end

text ‹TODO: It might make sense to have a class for preorders too,
analogous to class eq_equiv›.›

class Ord_linear = Ord_strict +
  assumes eq_conv_compare: "eqxy = is_EQ(comparexy)"
    and oppOrdering_compare [simp]:
    "oppOrdering(comparexy) = compareyx"
    and compare_EQ_dest: "comparexy = EQ  x = y"
    and compare_self_below_EQ: "comparexx  EQ"
    and compare_LT_trans:
    "comparexy = LT  compareyz = LT  comparexz = LT"
  (*BH: Is this set of axioms complete?*)
  (*CS: How about totality of the order?*)
begin

lemma eq_TT_dest: "eqxy = TT  x = y"
  by (cases "comparexy") (auto dest: compare_EQ_dest simp: eq_conv_compare)+

lemma le_iff_lt_or_eq:
  "lexy = TT  ltxy = TT  eqxy = TT"
  by (cases "comparexy") (simp add: le_def lt_def eq_conv_compare)+

lemma compare_sym:
  "comparexy = (case compareyx of LT  GT | EQ  EQ | GT  LT)"
  by (subst oppOrdering_compare [symmetric]) (simp add: oppOrdering_def)

lemma compare_self_neq_LT [simp]: "comparexx  LT"
  using compare_self_below_EQ [of x] by clarsimp

lemma compare_self_neq_GT [simp]: "comparexx  GT"
  using compare_self_below_EQ [of x] by clarsimp

declare compare_self_below_EQ [simp]

lemma lt_trans: "ltxy = TT  ltyz = TT  ltxz = TT"
  unfolding lt_eq_TT_iff by (rule compare_LT_trans)

lemma compare_GT_iff_LT: "comparexy = GT  compareyx = LT"
  by (cases "comparexy", simp_all add: compare_sym [of y x])

lemma compare_GT_trans:
  "comparexy = GT  compareyz = GT  comparexz = GT"
  unfolding compare_GT_iff_LT by (rule compare_LT_trans)

lemma compare_EQ_iff_eq_TT:
  "comparexy = EQ  eqxy = TT"
  by (cases "comparexy") (simp add: is_EQ_def eq_conv_compare)+

lemma compare_EQ_trans:
  "comparexy = EQ  compareyz = EQ  comparexz = EQ"
  by (blast dest: compare_EQ_dest)

lemma le_trans:
  "lexy = TT  leyz = TT  lexz = TT"
  by (auto dest: eq_TT_dest lt_trans simp: le_iff_lt_or_eq)

lemma neg_lt: "neg(ltxy) = leyx"
  by (cases "comparexy", simp_all add: le_def lt_def compare_sym [of y x])

lemma neg_le: "neg(lexy) = ltyx"
  by (cases "comparexy", simp_all add: le_def lt_def compare_sym [of y x])

subclass Eq_eq
proof
  fix x y
  show "eqxy = eqyx"
    unfolding eq_conv_compare
    by (cases "comparexy", simp_all add: compare_sym [of y x])
  show "eqx = "
    unfolding eq_conv_compare by simp
  show "eqy = "
    unfolding eq_conv_compare by simp
  show "eqxx  FF"
    unfolding eq_conv_compare
    by (cases "comparexx", simp_all)
  { assume "eqxy = TT" then show "x = y"
      unfolding eq_conv_compare
      by (cases "comparexy", auto dest: compare_EQ_dest) }
qed

end

text ‹A combinator for defining Ord instances for datatypes.›

definition thenOrdering :: "Ordering  Ordering  Ordering" where
  "thenOrdering = (Λ x y. case x of LT  LT | EQ  y | GT  GT)"

lemma thenOrdering_simps [simp]:
  "thenOrderingLTy = LT"
  "thenOrderingEQy = y"
  "thenOrderingGTy = GT"
  "thenOrderingy = "
  unfolding thenOrdering_def by simp_all

lemma thenOrdering_LT_iff [simp]:
  "thenOrderingxy = LT  x = LT  x = EQ  y = LT"
  by (cases x, simp_all)

lemma thenOrdering_EQ_iff [simp]:
  "thenOrderingxy = EQ  x = EQ  y = EQ"
  by (cases x, simp_all)

lemma thenOrdering_GT_iff [simp]:
  "thenOrderingxy = GT  x = GT  x = EQ  y = GT"
  by (cases x, simp_all)

lemma thenOrdering_below_EQ_iff [simp]:
  "thenOrderingxy  EQ  x  EQ  (x =   y  EQ)"
  by (cases x) simp_all

lemma is_EQ_thenOrdering [simp]:
  "is_EQ(thenOrderingxy) = (is_EQx andalso is_EQy)"
  by (cases x) simp_all

lemma oppOrdering_thenOrdering:
  "oppOrdering(thenOrderingxy) =
    thenOrdering(oppOrderingx)(oppOrderingy)"
  by (cases x) simp_all

instantiation lift :: ("{linorder,countable}") Ord_linear
begin

definition
  "compare  (Λ (Def x) (Def y).
    if x < y then LT else if x > y then GT else EQ)"

instance proof
  fix x y z :: "'a lift"
  show "comparey = "
    unfolding compare_lift_def by simp
  show "comparex = "
    unfolding compare_lift_def by (cases x, simp_all)
  show "oppOrdering(comparexy) = compareyx"
    unfolding compare_lift_def
    by (cases x, cases y, simp, simp,
      cases y, simp, simp add: not_less less_imp_le)
  { assume "comparexy = EQ" then show "x = y"
      unfolding compare_lift_def
      by (cases x, cases y, simp, simp,
        cases y, simp, simp split: if_splits) }
  { assume "comparexy = LT" and "compareyz = LT" then show "comparexz = LT"
      unfolding compare_lift_def
      by (cases x, simp, cases y, simp, cases z, simp,
        auto split: if_splits) }
  show "eqxy = is_EQ(comparexy)"
    unfolding eq_lift_def compare_lift_def
    by (cases x, simp, cases y, simp, auto)
  show "comparexx  EQ"
    unfolding compare_lift_def
    by (cases x, simp_all)
qed

end

lemma lt_le:
  "lt(x::'a::Ord_linear)y = (lexy andalso neqxy)"
  by (cases "comparexy")
     (auto simp: lt_def le_def eq_conv_compare)

end

Theory Numeral_Cpo

section ‹Cpo for Numerals›

theory Numeral_Cpo
  imports HOLCF_Main
begin

class plus_cpo = plus + cpo +
  assumes cont_plus1: "cont (λx::'a::{plus,cpo}. x + y)"
  assumes cont_plus2: "cont (λy::'a::{plus,cpo}. x + y)"
begin

abbreviation plus_section :: "'a  'a  'a" ("'(+')") where
  "(+)  Λ x y. x + y"

abbreviation plus_section_left :: "'a  'a  'a" ("'(_+')") where
  "(x+)  Λ y. x + y"

abbreviation plus_section_right :: "'a  'a  'a" ("'(+_')") where
  "(+y)  Λ x. x + y"

end

class minus_cpo = minus + cpo +
  assumes cont_minus1: "cont (λx::'a::{minus,cpo}. x - y)"
  assumes cont_minus2: "cont (λy::'a::{minus,cpo}. x - y)"
begin

abbreviation minus_section :: "'a  'a  'a" ("'(-')") where
  "(-)  Λ x y. x - y"

abbreviation minus_section_left :: "'a  'a  'a" ("'(_-')") where
  "(x-)  Λ y. x - y"

abbreviation minus_section_right :: "'a  'a  'a" ("'(-_')") where
  "(-y)  Λ x. x - y"

end

class times_cpo = times + cpo +
  assumes cont_times1: "cont (λx::'a::{times,cpo}. x * y)"
  assumes cont_times2: "cont (λy::'a::{times,cpo}. x * y)"
begin

(*
This clashes with comment syntax
abbreviation times_section :: "'a → 'a → 'a" ("'(?')") where
  "(?) ≡ Λ x y. x * y"

abbreviation times_section_left :: "'a ⇒ 'a → 'a" ("'(_?')") where
  "(x?) ≡ Λ y. x * y"

abbreviation times_section_right :: "'a ⇒ 'a → 'a" ("'(?_')") where
  "(?y) ≡ Λ x. x - y"
*)
end


lemma cont2cont_plus [simp, cont2cont]:
  assumes "cont (λx. f x)" and "cont (λx. g x)"
  shows "cont (λx. f x + g x :: 'a::plus_cpo)"
  apply (rule cont_apply [OF assms(1) cont_plus1])
  apply (rule cont_apply [OF assms(2) cont_plus2])
  apply (rule cont_const)
  done

lemma cont2cont_minus [simp, cont2cont]:
  assumes "cont (λx. f x)" and "cont (λx. g x)"
  shows "cont (λx. f x - g x :: 'a::minus_cpo)"
  apply (rule cont_apply [OF assms(1) cont_minus1])
  apply (rule cont_apply [OF assms(2) cont_minus2])
  apply (rule cont_const)
  done

lemma cont2cont_times [simp, cont2cont]:
  assumes "cont (λx. f x)" and "cont (λx. g x)"
  shows "cont (λx. f x * g x :: 'a::times_cpo)"
  apply (rule cont_apply [OF assms(1) cont_times1])
  apply (rule cont_apply [OF assms(2) cont_times2])
  apply (rule cont_const)
  done

instantiation u :: ("{zero,cpo}") zero
begin
  definition "zero_u = up(0::'a)"
  instance ..
end

instantiation u :: ("{one,cpo}") one
begin
  definition "one_u = up(1::'a)"
  instance ..
end

instantiation u :: (plus_cpo) plus
begin
  definition "plus_u x y = (Λ(upa) (upb). up(a + b))xy" for x y :: "'a"
  instance ..
end

instantiation u :: (minus_cpo) minus
begin
  definition "minus_u x y = (Λ(upa) (upb). up(a - b))xy" for x y :: "'a"
  instance ..
end

instantiation u :: (times_cpo) times
begin
  definition "times_u x y = (Λ(upa) (upb). up(a * b))xy" for x y :: "'a"
  instance ..
end

lemma plus_u_strict [simp]:
  fixes x :: "_ u" shows "x +  = " and " + x = "
  unfolding plus_u_def by (cases x, simp, simp)+

lemma minus_u_strict [simp]:
  fixes x :: "_ u" shows "x -  = " and " - x = "
  unfolding minus_u_def by (cases x, simp_all)+

lemma times_u_strict [simp]:
  fixes x :: "_ u" shows "x *  = " and " * x = "
  unfolding times_u_def by (cases x, simp_all)+

lemma plus_up_up [simp]: "upx + upy = up(x + y)"
  unfolding plus_u_def by simp

lemma minus_up_up [simp]: "upx - upy = up(x - y)"
  unfolding minus_u_def by simp

lemma times_up_up [simp]: "upx * upy = up(x * y)"
  unfolding times_u_def by simp

instance u :: (plus_cpo) plus_cpo
  by standard (simp_all add: plus_u_def)

instance u :: (minus_cpo) minus_cpo
  by standard (simp_all add: minus_u_def)

instance u :: (times_cpo) times_cpo
  by standard (simp_all add: times_u_def)

instance u :: ("{semigroup_add,plus_cpo}") semigroup_add
proof
  fix a b c :: "'a u"
  show "(a + b) + c = a + (b + c)"
    unfolding plus_u_def
    by (cases a; cases b; cases c) (simp_all add: add.assoc)
qed

instance u :: ("{ab_semigroup_add,plus_cpo}") ab_semigroup_add
proof
  fix a b :: "'a u"
  show "a + b = b + a"
    by (cases a; cases b) (simp_all add: add.commute)
qed

instance u :: ("{monoid_add,plus_cpo}") monoid_add
proof
  fix a :: "'a u"
  show "0 + a = a"
    unfolding zero_u_def by (cases a) simp_all
  show "a + 0 = a"
    unfolding zero_u_def by (cases a) simp_all
qed

instance u :: ("{comm_monoid_add,plus_cpo}") comm_monoid_add
proof
  fix a :: "'a u"
  show "0 + a = a"
    unfolding zero_u_def by (cases a) simp_all
qed

instance u :: ("{numeral, plus_cpo}") numeral ..

instance int :: plus_cpo
  by standard simp_all

instance int :: minus_cpo
  by standard simp_all

end

Theory Data_Function

section ‹Data: Functions›

theory Data_Function
  imports HOLCF_Main
begin

fixrec flip :: "('a -> 'b -> 'c) -> 'b -> 'a -> 'c" where
  "flipfxy = fyx"

fixrec const :: "'a  'b  'a" where
  "constx_ = x"

fixrec dollar :: "('a -> 'b) -> 'a -> 'b" where
  "dollarfx = fx"

fixrec dollarBang :: "('a -> 'b) -> 'a -> 'b" where
  "dollarBangfx = seqx(fx)"

fixrec on :: "('b -> 'b -> 'c) -> ('a -> 'b) -> 'a -> 'a -> 'c" where
  "ongfxy = g(fx)(fy)"

end

Theory Data_Bool

section ‹Data: Bool›

theory Data_Bool
  imports Type_Classes
begin

subsection ‹Class instances›

text ‹Eq›

lemma eq_eqI[case_names bottomLTR bottomRTL LTR RTL]:
  "(x =   y = )  (y =   x = )  (x = TT  y = TT)  (y = TT  x = TT)  x = y"
by (metis Exh_tr)

lemma eq_tr_simps [simp]:
  shows "eqTTTT = TT" and "eqTTFF = FF"
    and "eqFFTT = FF" and "eqFFFF = TT"
  unfolding TT_def FF_def eq_lift_def by simp_all

text ‹Ord›

lemma compare_tr_simps [simp]:
  "compareFFFF = EQ"
  "compareFFTT = LT"
  "compareTTFF = GT"
  "compareTTTT = EQ"
  unfolding TT_def FF_def compare_lift_def
  by simp_all

subsection ‹Lemmas›

lemma andalso_eq_TT_iff [simp]:
  "(x andalso y) = TT  x = TT  y = TT"
  by (cases x, simp_all)

lemma andalso_eq_FF_iff [simp]:
  "(x andalso y) = FF  x = FF  (x = TT  y = FF)"
  by (cases x, simp_all)

lemma andalso_eq_bottom_iff [simp]:
  "(x andalso y) =   x =   (x = TT  y = )"
  by (cases x, simp_all)

lemma orelse_eq_FF_iff [simp]:
  "(x orelse y) = FF  x = FF  y = FF"
  by (cases x, simp_all)

lemma orelse_assoc [simp]:
  "((x orelse y) orelse z) = (x orelse y orelse z)"
  by (cases x) auto

lemma andalso_assoc [simp]:
  "((x andalso y) andalso z) = (x andalso y andalso z)"
  by (cases x) auto

lemma neg_orelse [simp]:
  "neg(x orelse y) = (negx andalso negy)"
  by (cases x) auto

lemma neg_andalso [simp]:
  "neg(x andalso y) = (negx orelse negy)"
  by (cases x) auto

text ‹Not suitable as default simp rules, because they cause the
  simplifier to loop:›
lemma neg_eq_simps:
  "negx = TT  x = FF"
  "negx = FF  x = TT"
  "negx =   x = "
  by (cases x, simp_all)+

lemma neg_eq_TT_iff [simp]: "negx = TT  x = FF"
  by (cases x, simp_all)+

lemma neg_eq_FF_iff [simp]: "negx = FF  x = TT"
  by (cases x, simp_all)+

lemma neg_eq_bottom_iff [simp]: "negx =   x = "
  by (cases x, simp_all)+

(* TODO: set up reorient_proc for TT and FF *)

lemma neg_eq [simp]:
  "negx = negy  x = y"
  by (cases y, simp_all)

lemma neg_neg [simp]:
  "neg(negx) = x"
  by (cases x, auto)

lemma neg_comp_neg [simp]:
  "neg oo neg = ID"
  by (metis cfun_eqI cfcomp2 neg_neg ID1)

end

Theory Data_Tuple

section ‹Data: Tuple›

theory Data_Tuple
  imports
    Type_Classes
    Data_Bool
begin

subsection ‹Datatype definitions›

domain Unit ("⟨⟩") = Unit ("⟨⟩")

domain ('a, 'b) Tuple2 ("_, _") =
  Tuple2 (lazy fst :: "'a") (lazy snd :: "'b") ("_, _")

notation Tuple2 ("⟨,⟩")

fixrec uncurry :: "('a  'b  'c)  'a, 'b  'c"
  where "uncurryfp = f(fstp)(sndp)"

fixrec curry :: "('a, 'b   'c)  'a  'b  'c"
  where "curryfab = fa, b"

domain ('a, 'b, 'c) Tuple3 ("_, _, _") =
  Tuple3 (lazy "'a") (lazy "'b") (lazy "'c") ("_, _, _")

notation Tuple3 ("⟨,,⟩")

subsection ‹Type class instances›

instantiation Unit :: Ord_linear
begin

definition
  "eq = (Λ ⟨⟩ ⟨⟩. TT)"

definition
  "compare = (Λ ⟨⟩ ⟨⟩. EQ)"

instance
  apply standard
        apply (unfold eq_Unit_def compare_Unit_def)
        apply simp
       apply (rename_tac x, case_tac x, simp, simp)
      apply (rename_tac x y, case_tac x, simp, case_tac y, simp, simp)
     apply (rename_tac x y, case_tac x, case_tac y, simp, simp, case_tac y, simp, simp)
    apply (rename_tac x y, case_tac x, simp, case_tac y, simp, simp)
   apply (rename_tac x, case_tac x, simp, simp)
  apply (rename_tac x y z, case_tac x, simp, case_tac y, simp, case_tac z, simp, simp)
  done

end

instantiation Tuple2 :: (Eq, Eq) Eq_strict
begin

definition
  "eq = (Λ x1, y1 x2, y2. eqx1x2 andalso eqy1y2)"

instance proof
  fix x :: "'a, 'b"
  show "eqx = "
    unfolding eq_Tuple2_def by (cases x, simp_all)
  show "eqx = "
    unfolding eq_Tuple2_def by simp
qed

end

lemma eq_Tuple2_simps [simp]:
  "eqx1, y1x2, y2 = (eqx1x2 andalso eqy1y2)"
  unfolding eq_Tuple2_def by simp

instance Tuple2 :: (Eq_sym, Eq_sym) Eq_sym
proof
  fix x y :: "'a, 'b"
  show "eqxy = eqyx"
    unfolding eq_Tuple2_def
    by (cases x, simp, (cases y, simp, simp add: eq_sym)+)
qed

instance Tuple2 :: (Eq_equiv, Eq_equiv) Eq_equiv
proof
  fix x y z :: "'a, 'b"
  show "eqxx  FF"
    by (cases x, simp_all)
  { assume "eqxy = TT" and "eqyz = TT" then show "eqxz = TT"
      by (cases x, simp, cases y, simp, cases z, simp, simp,
          fast intro: eq_trans) }
qed

instance Tuple2 :: (Eq_eq, Eq_eq) Eq_eq
proof
  fix x y :: "'a, 'b"
  show "eqxx  FF"
    by (cases x, simp_all)
  { assume "eqxy = TT" then show "x = y"
      by (cases x, simp, cases y, simp, simp, fast) }
qed

instantiation Tuple2 :: (Ord, Ord) Ord_strict
begin

definition
  "compare = (Λ x1, y1 x2, y2.
    thenOrdering(comparex1x2)(comparey1y2))"

instance
  by standard (simp add: compare_Tuple2_def,
      rename_tac x, case_tac x, simp_all add: compare_Tuple2_def)

end

lemma compare_Tuple2_simps [simp]:
  "comparex1, y1x2, y2 = thenOrdering(comparex1x2)(comparey1y2)"
  unfolding compare_Tuple2_def by simp

instance Tuple2 :: (Ord_linear, Ord_linear) Ord_linear
proof
  fix x y z :: "'a, 'b"
  show "eqxy = is_EQ(comparexy)"
    by (cases x, simp, cases y, simp, simp add: eq_conv_compare)
  show "oppOrdering(comparexy) = compareyx"
    by (cases x, simp, cases y, simp, simp add: oppOrdering_thenOrdering)
  { assume "comparexy = EQ" then show "x = y"
      by (cases x, simp, cases y, simp, auto elim: compare_EQ_dest) }
  { assume "comparexy = LT" and "compareyz = LT" then show "comparexz = LT"
      apply (cases x, simp, cases y, simp, cases z, simp, simp)
      apply (elim disjE conjE)
         apply (fast elim!: compare_LT_trans)
        apply (fast dest: compare_EQ_dest)
       apply (fast dest: compare_EQ_dest)
      apply (drule compare_EQ_dest)
      apply (fast elim!: compare_LT_trans)
      done }
  show "comparexx  EQ"
    by (cases x, simp_all)
qed

instantiation Tuple3 :: (Eq, Eq, Eq) Eq_strict
begin

definition
  "eq = (Λ x1, y1, z1 x2, y2, z2.
    eqx1x2 andalso eqy1y2 andalso eqz1z2)"

instance proof
  fix x :: "'a, 'b, 'c"
  show "eqx = "
    unfolding eq_Tuple3_def by (cases x, simp_all)
  show "eqx = "
    unfolding eq_Tuple3_def by simp
qed

end

lemma eq_Tuple3_simps [simp]:
  "eqx1, y1, z1x2, y2, z2 = (eqx1x2 andalso eqy1y2 andalso eqz1z2)"
  unfolding eq_Tuple3_def by simp

instance Tuple3 :: (Eq_sym, Eq_sym, Eq_sym) Eq_sym
proof
  fix x y :: "'a, 'b, 'c"
  show "eqxy = eqyx"
    unfolding eq_Tuple3_def
    by (cases x, simp, (cases y, simp, simp add: eq_sym)+)
qed

instance Tuple3 :: (Eq_equiv, Eq_equiv, Eq_equiv) Eq_equiv
proof
  fix x y z :: "'a, 'b, 'c"
  show "eqxx  FF"
    by (cases x, simp_all)
  { assume "eqxy = TT" and "eqyz = TT" then show "eqxz = TT"
      by (cases x, simp, cases y, simp, cases z, simp, simp,
          fast intro: eq_trans) }
qed

instance Tuple3 :: (Eq_eq, Eq_eq, Eq_eq) Eq_eq
proof
  fix x y :: "'a, 'b, 'c"
  show "eqxx  FF"
    by (cases x, simp_all)
  { assume "eqxy = TT" then show "x = y"
      by (cases x, simp, cases y, simp, simp, fast) }
qed

instantiation Tuple3 :: (Ord, Ord, Ord) Ord_strict
begin

definition
  "compare = (Λ x1, y1, z1 x2, y2, z2.
    thenOrdering(comparex1x2)(thenOrdering(comparey1y2)(comparez1z2)))"

instance
  by standard (simp add: compare_Tuple3_def,
    rename_tac x, case_tac x, simp_all add: compare_Tuple3_def)

end

lemma compare_Tuple3_simps [simp]:
  "comparex1, y1, z1x2, y2, z2 =
    thenOrdering(comparex1x2)
      (thenOrdering(comparey1y2)(comparez1z2))"
  unfolding compare_Tuple3_def by simp

instance Tuple3 :: (Ord_linear, Ord_linear, Ord_linear) Ord_linear
proof
  fix x y z :: "'a, 'b, 'c"
  show "eqxy = is_EQ(comparexy)"
    by (cases x, simp, cases y, simp, simp add: eq_conv_compare)
  show "oppOrdering(comparexy) = compareyx"
    by (cases x, simp, cases y, simp, simp add: oppOrdering_thenOrdering)
  { assume "comparexy = EQ" then show "x = y"
      by (cases x, simp, cases y, simp, auto elim: compare_EQ_dest) }
  { assume "comparexy = LT" and "compareyz = LT" then show "comparexz = LT"
      apply (cases x, simp, cases y, simp, cases z, simp, simp)
      apply (elim disjE conjE)
              apply (fast elim!: compare_LT_trans)
             apply (fast dest: compare_EQ_dest)
            apply (fast dest: compare_EQ_dest)
           apply (fast dest: compare_EQ_dest)
          apply (fast dest: compare_EQ_dest)
         apply (drule compare_EQ_dest)
         apply (fast elim!: compare_LT_trans)
        apply (fast dest: compare_EQ_dest)
       apply (fast dest: compare_EQ_dest)
      apply (fast dest: compare_EQ_dest elim!: compare_LT_trans)
      done }
  show "comparexx  EQ"
    by (cases x, simp_all)
qed

end

Theory Data_Integer

section ‹Data: Integers›

theory Data_Integer
  imports
    Numeral_Cpo
    Data_Bool
begin

domain Integer = MkI (lazy int)

instance Integer :: flat
proof
  fix x y :: Integer
  assume "x  y" then show "x =   x = y"
    by (cases x; cases y) simp_all
qed

instantiation Integer :: "{plus,times,minus,uminus,zero,one}"
begin

definition "0 = MkI0"
definition "1 = MkI1"
definition "a + b = (Λ (MkIx) (MkIy). MkI(x + y))ab"
definition "a - b = (Λ (MkIx) (MkIy). MkI(x - y))ab"
definition "a * b = (Λ (MkIx) (MkIy). MkI(x * y))ab"
definition "- a = (Λ (MkIx). MkI(uminus x))a"

instance ..

end

lemma Integer_arith_strict [simp]:
  fixes x :: Integer
  shows " + x = " and "x +  = "
    and " * x = " and "x *  = "
    and " - x = " and "x -  = "
    and "-  = (::Integer)"
  unfolding plus_Integer_def times_Integer_def
  unfolding minus_Integer_def uminus_Integer_def
  by (cases x, simp, simp)+

lemma Integer_arith_simps [simp]:
  "MkIa + MkIb = MkI(a + b)"
  "MkIa * MkIb = MkI(a * b)"
  "MkIa - MkIb = MkI(a - b)"
  "- MkIa = MkI(uminus a)"
  unfolding plus_Integer_def times_Integer_def
  unfolding minus_Integer_def uminus_Integer_def
  by simp_all

lemma plus_MkI_MkI:
  "MkIx + MkIy = MkI(x + y)"
  unfolding plus_Integer_def by simp

instance Integer :: "{plus_cpo,minus_cpo,times_cpo}"
  by standard (simp_all add: flatdom_strict2cont)

instance Integer :: comm_monoid_add
proof
  fix a b c :: Integer
  show "(a + b) + c = a + (b + c)"
    by (cases a; cases b; cases c) simp_all
  show "a + b = b + a"
    by (cases a; cases b) simp_all
  show "0 + a = a"
    unfolding zero_Integer_def
    by (cases a) simp_all
qed

instance Integer :: comm_monoid_mult
proof
  fix a b c :: Integer
  show "(a * b) * c = a * (b * c)"
    by (cases a; cases b; cases c) simp_all
  show "a * b = b * a"
    by (cases a; cases b) simp_all
  show "1 * a = a"
    unfolding one_Integer_def
    by (cases a) simp_all
qed

instance Integer :: comm_semiring
proof
  fix a b c :: Integer
  show "(a + b) * c = a * c + b * c"
    by (cases a; cases b; cases c) (simp_all add: distrib_right)
qed

instance Integer :: semiring_numeral ..

lemma Integer_add_diff_cancel [simp]:
  "b    (a::Integer) + b - b = a"
  by (cases a; cases b) simp_all

lemma zero_Integer_neq_bottom [simp]: "(0::Integer)  "
  by (simp add: zero_Integer_def)

lemma one_Integer_neq_bottom [simp]: "(1::Integer)  "
  by (simp add: one_Integer_def)

lemma plus_Integer_eq_bottom_iff [simp]:
  fixes x y :: Integer shows "x + y =   x =   y = "
  by (cases x, simp, cases y, simp, simp)

lemma diff_Integer_eq_bottom_iff [simp]:
  fixes x y :: Integer shows "x - y =   x =   y = "
  by (cases x, simp, cases y, simp, simp)

lemma mult_Integer_eq_bottom_iff [simp]:
  fixes x y :: Integer shows "x * y =   x =   y = "
  by (cases x, simp, cases y, simp, simp)

lemma minus_Integer_eq_bottom_iff [simp]:
  fixes x :: Integer shows "- x =   x = "
  by (cases x, simp, simp)

lemma numeral_Integer_eq: "numeral k = MkI(numeral k)"
  by (induct k, simp_all only: numeral.simps one_Integer_def plus_MkI_MkI)

lemma numeral_Integer_neq_bottom [simp]: "(numeral k::Integer)  "
  unfolding numeral_Integer_eq by simp

text ‹Symmetric versions are also needed, because the reorient
  simproc does not apply to these comparisons.›

lemma bottom_neq_zero_Integer [simp]: "(::Integer)  0"
  by (simp add: zero_Integer_def)

lemma bottom_neq_one_Integer [simp]: "(::Integer)  1"
  by (simp add: one_Integer_def)

lemma bottom_neq_numeral_Integer [simp]: "(::Integer)  numeral k"
  unfolding numeral_Integer_eq by simp

instantiation Integer :: Ord_linear
begin

definition
  "eq = (Λ (MkIx) (MkIy). if x = y then TT else FF)"

definition
  "compare = (Λ (MkIx) (MkIy).
    if x < y then LT else if x > y then GT else EQ)"

instance proof
  fix x y z :: Integer
  show "comparey = "
    unfolding compare_Integer_def by simp
  show "comparex = "
    unfolding compare_Integer_def by (cases x, simp_all)
  show "oppOrdering(comparexy) = compareyx"
    unfolding compare_Integer_def
    by (cases x, cases y, simp, simp,
        cases y, simp, simp add: not_less less_imp_le)
  { assume "comparexy = EQ" then show "x = y"
      unfolding compare_Integer_def
      by (cases x, cases y, simp, simp,
          cases y, simp, simp split: if_splits) }
  { assume "comparexy = LT" and "compareyz = LT" then show "comparexz = LT"
      unfolding compare_Integer_def
      by (cases x, simp, cases y, simp, cases z, simp,
          auto split: if_splits) }
  show "eqxy = is_EQ(comparexy)"
    unfolding eq_Integer_def compare_Integer_def
    by (cases x, simp, cases y, simp, auto)
  show "comparexx  EQ"
    unfolding compare_Integer_def
    by (cases x, simp_all)
qed

end

lemma eq_MkI_MkI [simp]:
  "eq(MkIm)(MkIn) = (if m = n then TT else FF)"
  by (simp add: eq_Integer_def)

lemma compare_MkI_MkI [simp]:
  "compare(MkIx)(MkIy) = (if x < y then LT else if x > y then GT else EQ)"
  unfolding compare_Integer_def by simp

lemma lt_MkI_MkI [simp]:
  "lt(MkIx)(MkIy) = (if x < y then TT else FF)"
  unfolding lt_def by simp

lemma le_MkI_MkI [simp]:
  "le(MkIx)(MkIy) = (if x  y then TT else FF)"
  unfolding le_def by simp

lemma eq_Integer_bottom_iff [simp]:
  fixes x y :: Integer shows "eqxy =   x =   y = "
  by (cases x, simp, cases y, simp, simp)

lemma compare_Integer_bottom_iff [simp]:
  fixes x y :: Integer shows "comparexy =   x =   y = "
  by (cases x, simp, cases y, simp, simp)

lemma lt_Integer_bottom_iff [simp]:
  fixes x y :: Integer shows "ltxy =   x =   y = "
  by (cases x, simp, cases y, simp, simp)

lemma le_Integer_bottom_iff [simp]:
  fixes x y :: Integer shows "lexy =   x =   y = "
  by (cases x, simp, cases y, simp, simp)

lemma compare_refl_Integer [simp]:
  "(x::Integer)    comparexx = EQ"
  by (cases x) simp_all

lemma eq_refl_Integer [simp]:
  "(x::Integer)    eqxx = TT"
  by (cases x) simp_all

lemma lt_refl_Integer [simp]:
  "(x::Integer)    ltxx = FF"
  by (cases x) simp_all

lemma le_refl_Integer [simp]:
  "(x::Integer)    lexx = TT"
  by (cases x) simp_all

lemma eq_Integer_numeral_simps [simp]:
  "eq(0::Integer)0 = TT"
  "eq(0::Integer)1 = FF"
  "eq(1::Integer)0 = FF"
  "eq(1::Integer)1 = TT"
  "eq(0::Integer)(numeral k) = FF"
  "eq(numeral k)(0::Integer) = FF"
  "k  Num.One  eq(1::Integer)(numeral k) = FF"
  "k  Num.One  eq(numeral k)(1::Integer) = FF"
  "eq(numeral k::Integer)(numeral l) = (if k = l then TT else FF)"
  unfolding zero_Integer_def one_Integer_def numeral_Integer_eq
  by simp_all

lemma compare_Integer_numeral_simps [simp]:
  "compare(0::Integer)0 = EQ"
  "compare(0::Integer)1 = LT"
  "compare(1::Integer)0 = GT"
  "compare(1::Integer)1 = EQ"
  "compare(0::Integer)(numeral k) = LT"
  "compare(numeral k)(0::Integer) = GT"
  "Num.One < k  compare(1::Integer)(numeral k) = LT"
  "Num.One < k  compare(numeral k)(1::Integer) = GT"
  "compare(numeral k::Integer)(numeral l) =
    (if k < l then LT else if k > l then GT else EQ)"
  unfolding zero_Integer_def one_Integer_def numeral_Integer_eq
  by simp_all

lemma lt_Integer_numeral_simps [simp]:
  "lt(0::Integer)0 = FF"
  "lt(0::Integer)1 = TT"
  "lt(1::Integer)0 = FF"
  "lt(1::Integer)1 = FF"
  "lt(0::Integer)(numeral k) = TT"
  "lt(numeral k)(0::Integer) = FF"
  "Num.One < k  lt(1::Integer)(numeral k) = TT"
  "lt(numeral k)(1::Integer) = FF"
  "lt(numeral k::Integer)(numeral l) = (if k < l then TT else FF)"
  unfolding zero_Integer_def one_Integer_def numeral_Integer_eq
  by simp_all

lemma le_Integer_numeral_simps [simp]:
  "le(0::Integer)0 = TT"
  "le(0::Integer)1 = TT"
  "le(1::Integer)0 = FF"
  "le(1::Integer)1 = TT"
  "le(0::Integer)(numeral k) = TT"
  "le(numeral k)(0::Integer) = FF"
  "le(1::Integer)(numeral k) = TT"
  "Num.One < k  le(numeral k)(1::Integer) = FF"
  "le(numeral k::Integer)(numeral l) = (if k  l then TT else FF)"
  unfolding zero_Integer_def one_Integer_def numeral_Integer_eq
  by simp_all

lemma MkI_eq_0_iff [simp]: "MkIn = 0  n = 0"
  unfolding zero_Integer_def by simp

lemma MkI_eq_1_iff [simp]: "MkIn = 1  n = 1"
  unfolding one_Integer_def by simp

lemma MkI_eq_numeral_iff [simp]: "MkIn = numeral k  n = numeral k"
  unfolding numeral_Integer_eq by simp

lemma MkI_0: "MkI0 = 0"
  by simp

lemma MkI_1: "MkI1 = 1"
  by simp

lemma le_plus_1:
  fixes m :: "Integer"
  assumes "lemn = TT"
  shows "lem(n + 1) = TT"
proof -
  from assms have "n  " by auto
  then have "len(n + 1) = TT"
    by (cases n) (auto, metis le_MkI_MkI less_add_one less_le_not_le one_Integer_def plus_MkI_MkI)
  with assms show ?thesis by (auto dest: le_trans)
qed

subsection ‹Induction rules that do not break the abstraction›

lemma nonneg_Integer_induct [consumes 1, case_names 0 step]:
  fixes i :: Integer
  assumes i_nonneg: "le0i = TT"
    and zero: "P 0"
    and step: "i. le1i = TT  P (i - 1)  P i"
  shows "P i"
proof (cases i)
  case bottom
  then have False using i_nonneg by simp
  then show ?thesis ..
next
  case (MkI integer)
  show ?thesis
  proof (cases integer)
    case neg
    then have False using i_nonneg MkI by (auto simp add: zero_Integer_def one_Integer_def)
    then show ?thesis ..
  next
    case (nonneg nat)
    have "P (MkI(int nat))"
    proof(induct nat)
      case 0
      show ?case using zero by (simp add: zero_Integer_def)
    next
      case (Suc nat)
      have "le1(MkI(int (Suc nat))) = TT"
        by (simp add: one_Integer_def)
      moreover
      have "P (MkI(int (Suc nat)) - 1)"
        using Suc
        by (simp add: one_Integer_def)
      ultimately
      show ?case
        by (rule step)
    qed
    then show ?thesis using nonneg MkI by simp
  qed
qed

end

Theory Data_List

section ‹Data: List›

theory Data_List
  imports
    Type_Classes
    Data_Function
    Data_Bool
    Data_Tuple
    Data_Integer
    Numeral_Cpo
begin

no_notation (ASCII)
  Set.member  ("'(:')") and
  Set.member  ("(_/ : _)" [51, 51] 50)

subsection ‹Datatype definition›

domain 'a list ("[_]") =
  Nil ("[]") |
  Cons (lazy head :: 'a) (lazy tail :: "['a]") (infixr ":" 65)
(*FIXME: We need to standardize a mapping from Haskell fixities
(0 to 9) to Isabelle ones (between 50 and 100).*)


subsubsection ‹Section syntax for @{const Cons}

syntax
  "_Cons_section" :: "'a  ['a]  ['a]" ("'(:')")
  "_Cons_section_left" :: "'a  ['a]  ['a]" ("'(_:')")
translations
  "(x:)" == "(CONST Rep_cfun) (CONST Cons) x"

abbreviation Cons_section_right :: "['a]  'a  ['a]" ("'(:_')") where
  "(:xs)  Λ x. x:xs"

syntax
  "_lazy_list" :: "args  ['a]" ("[(_)]")
translations
  "[x, xs]" == "x : [xs]"
  "[x]" == "x : []"

abbreviation null :: "['a]  tr" where "null  is_Nil"


subsection ‹Haskell function definitions›

instantiation list :: (Eq) Eq_strict
begin

fixrec eq_list :: "['a]  ['a]  tr" where
  "eq_list[][] = TT" |
  "eq_list(x : xs)[] = FF" |
  "eq_list[](y : ys) = FF" |
  "eq_list(x : xs)(y : ys) = (eqxy andalso eq_listxsys)"

instance proof
  fix xs :: "['a]"
  show "eqxs = "
    by (cases xs, fixrec_simp+)
  show "eqxs = "
    by fixrec_simp
qed

end

instance list :: (Eq_sym) Eq_sym
proof
  fix xs ys :: "['a]"
  show "eqxsys = eqysxs"
  proof (induct xs arbitrary: ys)
    case Nil
    show ?case by (cases ys; simp)
  next
    case Cons
    then show ?case by (cases ys; simp add: eq_sym)
  qed simp_all
qed

instance list :: (Eq_equiv) Eq_equiv
proof
  fix xs ys zs :: "['a]"
  show "eqxsxs  FF"
    by (induct xs, simp_all)
  assume "eqxsys = TT" and "eqyszs = TT" then show "eqxszs = TT"
  proof (induct xs arbitrary: ys zs)
    case (Nil ys zs) then show ?case by (cases ys, simp_all)
  next
    case (Cons x xs ys zs)
    from Cons.prems show ?case
      by (cases ys; cases zs) (auto intro: eq_trans Cons.hyps)
  qed simp_all
qed

instance list :: (Eq_eq) Eq_eq
proof
  fix xs ys :: "['a]"
  show "eqxsxs  FF"
    by (induct xs) simp_all
  assume "eqxsys = TT" then show "xs = ys"
  proof (induct xs arbitrary: ys)
    case Nil
    then show ?case by (cases ys) auto
  next
    case Cons
    then show ?case by (cases ys) auto
  qed auto
qed

instantiation list :: (Ord) Ord_strict
begin

fixrec compare_list :: "['a]  ['a]  Ordering" where
  "compare_list[][] = EQ" |
  "compare_list(x : xs)[] = GT" |
  "compare_list[](y : ys) = LT" |
  "compare_list(x : xs)(y : ys) =
    thenOrdering(comparexy)(compare_listxsys)"

instance
  by standard (fixrec_simp, rename_tac x, case_tac x, fixrec_simp+)

end

instance list :: (Ord_linear) Ord_linear
proof
  fix xs ys zs :: "['a]"
  show "oppOrdering(comparexsys) = compareysxs"
  proof (induct xs arbitrary: ys)
    case Nil
    show ?case by (cases ys; simp)
  next
    case Cons
    then show ?case by (cases ys; simp add: oppOrdering_thenOrdering)
  qed simp_all
  show "xs = ys" if "comparexsys = EQ"
    using that
  proof (induct xs arbitrary: ys)
    case Nil
    then show ?case by (cases ys; simp)
  next
    case Cons
    then show ?case by (cases ys; auto elim: compare_EQ_dest)
  qed simp_all
  show "comparexszs = LT" if "comparexsys = LT" and "compareyszs = LT"
    using that
  proof (induct xs arbitrary: ys zs)
    case Nil
    then show ?case by (cases ys; cases zs; simp)
  next
    case (Cons a xs)
    then show ?case
      by (cases ys; cases zs) (auto dest: compare_EQ_dest compare_LT_trans)
  qed simp_all
  show "eqxsys = is_EQ(comparexsys)"
  proof (induct xs arbitrary: ys)
    case Nil
    show ?case by (cases ys; simp)
  next
    case Cons
    then show ?case by (cases ys; simp add: eq_conv_compare)
  qed simp_all
  show "comparexsxs  EQ"
    by (induct xs) simp_all
qed

fixrec zipWith :: "('a  'b  'c)  ['a]  ['b]  ['c]" where
  "zipWithf(x : xs)(y : ys) = fxy : zipWithfxsys" |
  "zipWithf(x : xs)[] = []" |
  "zipWithf[]ys = []"

definition zip :: "['a]  ['b]  ['a, 'b]" where
  "zip = zipWith⟨,⟩"

fixrec zipWith3 :: "('a  'b  'c  'd)  ['a]  ['b]  ['c]  ['d]" where
  "zipWith3f(x : xs)(y : ys)(z : zs) = fxyz : zipWith3fxsyszs" |
  (unchecked) "zipWith3fxsyszs = []"

definition zip3 :: "['a]  ['b]  ['c]  ['a, 'b, 'c]" where
  "zip3 = zipWith3⟨,,⟩"

fixrec map :: "('a  'b)  ['a]  ['b]" where
  "mapf[] = []" |
  "mapf(x : xs) = fx : mapfxs"

fixrec filter :: "('a  tr)  ['a]  ['a]" where
  "filterP[] = []" |
  "filterP(x : xs) =
    If (Px) then x : filterPxs else filterPxs"

fixrec repeat :: "'a  ['a]" where
  [simp del]: "repeatx = x : repeatx"

fixrec takeWhile :: "('a  tr)  ['a]  ['a]" where
 "takeWhilep[]     =  []" |
 "takeWhilep(x:xs) = If px then x : takeWhilepxs else  []"

fixrec dropWhile :: "('a  tr)  ['a]  ['a]" where
 "dropWhilep[]     =  []" |
 "dropWhilep(x:xs) = If px then dropWhilepxs else (x:xs)"

fixrec span :: "('a -> tr) -> ['a] -> ['a],['a]" where
 "spanp[]     = [],[]" |
 "spanp(x:xs) = If px then (case spanpxs of ys, zs  x:ys,zs) else [], x:xs"

fixrec break :: "('a -> tr) -> ['a] -> ['a],['a]" where
 "breakp = span(neg oo p)"

fixrec nth :: "['a]  Integer  'a" where
  "nth[]n = " |
  nth_Cons [simp del]:
  "nth(x : xs)n = If eqn0 then x else nthxs(n - 1)"
(* bh: Perhaps we should rename this to 'index',
to match the standard Haskell function named 'genericIndex'. *)

abbreviation nth_syn :: "['a]  Integer  'a" (infixl "!!" 100) where
  "xs !! n  nthxsn"

definition partition :: "('a  tr)  ['a]  ['a], ['a]" where
  "partition = (Λ P xs. filterPxs, filter(neg oo P)xs)"

fixrec iterate :: "('a  'a)  'a  ['a]" where
  "iteratefx = x : iteratef(fx)"

fixrec foldl ::  "('a -> 'b -> 'a) -> 'a -> ['b] -> 'a" where
  "foldlfz[]     = z" |
  "foldlfz(x:xs) = foldlf(fzx)xs"

fixrec foldl1 ::  "('a -> 'a -> 'a) -> ['a] -> 'a" where
  "foldl1f[]     = " |
  "foldl1f(x:xs) = foldlfxxs"

fixrec foldr :: "('a  'b  'b)  'b  ['a]  'b" where
  "foldrfd[] = d" |
  "foldrfd(x : xs) = fx(foldrfdxs)"

fixrec foldr1 :: "('a  'a  'a)  ['a]  'a" where
  "foldr1f[] = " |
  "foldr1f[x] = x" |
  "foldr1f(x : (x':xs)) = fx(foldr1f(x':xs))"

fixrec elem :: "'a::Eq  ['a]  tr" where
  "elemx[] = FF" |
  "elemx(y : ys) = (eqyx orelse elemxys)"

fixrec notElem :: "'a::Eq  ['a]  tr" where
  "notElemx[] = TT" |
  "notElemx(y : ys) = (neqyx andalso notElemxys)"

fixrec append :: "['a]  ['a]  ['a]" where
  "append[]ys = ys" |
  "append(x : xs)ys = x : appendxsys"

abbreviation append_syn :: "['a]  ['a]  ['a]" (infixr "++" 65) where
  "xs ++ ys  appendxsys"

definition concat :: "[['a]]  ['a]" where
  "concat = foldrappend[]"

definition concatMap :: "('a  ['b])  ['a]  ['b]" where
  "concatMap = (Λ f. concat oo mapf)"

fixrec last :: "['a] -> 'a" where
  "last[x] = x" |
  "last(_:(x:xs)) = last(x:xs)"

fixrec init :: "['a] -> ['a]" where
  "init[x] = []" |
  "init(x:(y:xs)) = x:(init(y:xs))"

fixrec reverse :: "['a] -> ['a]" where
  [simp del]:"reverse = foldl(flip(:))[]"

fixrec the_and :: "[tr]  tr" where
  "the_and = foldrtrandTT"

fixrec the_or :: "[tr]  tr" where
  "the_or = foldrtrorFF"

fixrec all :: "('a  tr)  ['a]  tr" where
  "allP = the_and oo (mapP)"

fixrec any :: "('a  tr)  ['a]  tr" where
  "anyP = the_or oo (mapP)"

fixrec tails :: "['a]  [['a]]" where
  "tails[] = [[]]" |
  "tails(x : xs) = (x : xs) : tailsxs"

fixrec inits :: "['a]  [['a]]" where
  "inits[] = [[]]" |
  "inits(x : xs) = [[]] ++ map(x:)(initsxs)"

fixrec scanr :: "('a  'b  'b)  'b  ['a]  ['b]"
where
  "scanrfq0[] = [q0]" |
  "scanrfq0(x : xs) = (
    let qs = scanrfq0xs in
    (case qs of
      []  
    | q : qs'  fxq : qs))"

fixrec scanr1 :: "('a  'a  'a)  ['a]  ['a]"
where
  "scanr1f[] = []" |
  "scanr1f(x : xs) =
    (case xs of
      []  [x]
    | x' : xs'  (
      let qs = scanr1fxs in
      (case qs of
        []  
      | q : qs'  fxq : qs)))"

fixrec scanl :: "('a  'b  'a)  'a  ['b]  ['a]" where
  "scanlfqls = q : (case ls of
    []  []
  | x : xs  scanlf(fqx)xs)"

definition scanl1 :: "('a  'a  'a)  ['a]  ['a]" where
  "scanl1 = (Λ f ls. (case ls of
    []  []
  | x : xs  scanlfxxs))"

subsubsection ‹Arithmetic Sequences›

fixrec upto :: "Integer  Integer  [Integer]" where
  [simp del]: "uptoxy = If lexy then x : upto(x+1)y else []"

fixrec intsFrom :: "Integer  [Integer]" where
  [simp del]: "intsFromx = seqx(x : intsFrom(x+1))"

class Enum =
  fixes toEnum :: "Integer  'a"
    and fromEnum :: "'a  Integer"
begin

definition succ :: "'a  'a" where
  "succ = toEnum oo (+1) oo fromEnum"

definition pred :: "'a  'a" where
  "pred = toEnum oo (-1) oo fromEnum"

definition enumFrom :: "'a  ['a]" where
  "enumFrom = (Λ x. maptoEnum(intsFrom(fromEnumx)))"

definition enumFromTo :: "'a  'a  ['a]" where
  "enumFromTo = (Λ x y. maptoEnum(upto(fromEnumx)(fromEnumy)))"

end

abbreviation enumFrom_To_syn :: "'a::Enum  'a  ['a]" ("(1[_../_])") where
  "[m..n]  enumFromTomn"

abbreviation enumFrom_syn :: "'a::Enum  ['a]" ("(1[_..])") where
  "[n..]  enumFromn"

instantiation Integer :: Enum
begin
definition [simp]: "toEnum = ID"
definition [simp]: "fromEnum = ID"
instance ..
end

fixrec take :: "Integer  ['a]  ['a]" where
  [simp del]: "takenxs = If len0 then [] else
    (case xs of []  [] | y : ys  y : take(n - 1)ys)"

fixrec drop :: "Integer  ['a]  ['a]" where
  [simp del]: "dropnxs = If len0 then xs else
    (case xs of []  [] | y : ys  drop(n - 1)ys)"

fixrec isPrefixOf :: "['a::Eq]  ['a]  tr" where
  "isPrefixOf[]_ = TT" |
  "isPrefixOf(x:xs)[] = FF" |
  "isPrefixOf(x:xs)(y:ys) = (eqxy andalso isPrefixOfxsys)"

fixrec isSuffixOf :: "['a::Eq]  ['a]  tr" where
  "isSuffixOfxy = isPrefixOf(reversex)(reversey)"

fixrec intersperse :: "'a  ['a]  ['a]" where
  "interspersesep[] = []" |
  "interspersesep[x] = [x]" |
  "interspersesep(x:y:xs) = x:sep:interspersesep(y:xs)"

fixrec intercalate :: "['a]  [['a]]  ['a]" where
  "intercalatexsxss = concat(interspersexsxss)"

definition replicate :: "Integer  'a  ['a]" where
  "replicate = (Λ n x. taken(repeatx))"

definition findIndices :: "('a  tr)  ['a]  [Integer]" where
  "findIndices = (Λ P xs.
    mapsnd(filter(Λ x, i. Px)(zipxs[0..])))"

fixrec length :: "['a]  Integer" where
  "length[] = 0" |
  "length(x : xs) = lengthxs + 1"

fixrec delete :: "'a::Eq  ['a]  ['a]" where
  "deletex[] = []" |
  "deletex(y : ys) = If eqxy then ys else y : deletexys"

fixrec diff :: "['a::Eq]  ['a]  ['a]" where
  "diffxs[] = xs" |
  "diffxs(y : ys) = diff(deleteyxs)ys"

abbreviation diff_syn :: "['a::Eq]  ['a]  ['a]" (infixl "\\\\" 70) where
  "xs \\\\ ys  diffxsys"


subsection ‹Logical predicates on lists›

inductive finite_list :: "['a]  bool" where
  Nil [intro!, simp]: "finite_list []" |
  Cons [intro!, simp]: "x xs. finite_list xs  finite_list (x : xs)"

inductive_cases finite_listE [elim!]: "finite_list (x : xs)"

lemma finite_list_upwards:
  assumes "finite_list xs" and "xs  ys"
  shows "finite_list ys"
using assms
proof (induct xs arbitrary: ys)
  case Nil
  then have "ys = []" by (cases ys) simp+
  then show ?case by auto
next
  case (Cons x xs)
  from x : xs  ys obtain y ys' where "ys = y : ys'" by (cases ys) auto
  with x : xs  ys have "xs  ys'" by auto
  then have "finite_list ys'" by (rule Cons.hyps)
  with ys = _ show ?case by auto
qed

lemma adm_finite_list [simp]: "adm finite_list"
  by (metis finite_list_upwards adm_upward)

lemma bot_not_finite_list [simp]:
  "finite_list  = False"
  by (rule, cases rule: finite_list.cases) auto

inductive listmem :: "'a  ['a]  bool" where
  "listmem x (x : xs)" |
  "listmem x xs  listmem x (y : xs)"

lemma listmem_simps [simp]:
  shows "¬ listmem x " and "¬ listmem x []"
  and "listmem x (y : ys)  x = y  listmem x ys"
  by (auto elim: listmem.cases intro: listmem.intros)

definition set :: "['a]  'a set" where
  "set xs = {x. listmem x xs}"

lemma set_simps [simp]:
  shows "set  = {}" and "set [] = {}"
  and "set (x : xs) = insert x (set xs)"
  unfolding set_def by auto

inductive distinct :: "['a]  bool" where
  Nil [intro!, simp]: "distinct []" |
  Cons [intro!, simp]: "x xs. distinct xs  x  set xs  distinct (x : xs)"


subsection ‹Properties›

lemma map_strict [simp]:
  "mapP = "
  by (fixrec_simp)

lemma map_ID [simp]:
  "mapIDxs = xs"
  by (induct xs) simp_all

lemma enumFrom_intsFrom_conv [simp]:
  "enumFrom = intsFrom"
  by (intro cfun_eqI) (simp add: enumFrom_def)

lemma enumFromTo_upto_conv [simp]:
  "enumFromTo = upto"
  by (intro cfun_eqI) (simp add: enumFromTo_def)

lemma zipWith_strict [simp]:
  "zipWithfys = "
  "zipWithf(x : xs) = "
  by fixrec_simp+

lemma zip_simps [simp]:
  "zip(x : xs)(y : ys) = x, y : zipxsys"
  "zip(x : xs)[] = []"
  "zip(x : xs) = "
  "zip[]ys = []"
  "zipys = "
  unfolding zip_def by simp_all

lemma zip_Nil2 [simp]:
  "xs    zipxs[] = []"
  by (cases xs) simp_all

lemma nth_strict [simp]:
  "nthn = "
  "nthxs = "
  by (fixrec_simp) (cases xs, fixrec_simp+)

lemma upto_strict [simp]:
  "uptoy = "
  "uptox = "
  by fixrec_simp+

lemma upto_simps [simp]:
  "n < m  upto(MkIm)(MkIn) = []"
  "m  n  upto(MkIm)(MkIn) = MkIm : [MkIm+1..MkIn]"
  by (subst upto.simps, simp)+

lemma filter_strict [simp]:
  "filterP = "
  by (fixrec_simp)

lemma nth_Cons_simp [simp]:
  "eqn0 = TT  nth(x : xs)n = x"
  "eqn0 = FF  nth(x : xs)n = nthxs(n - 1)"
  by (subst nth.simps, simp)+

lemma nth_Cons_split:
   "P (nth(x : xs)n) = ((eqn0 = FF  P (nth(x : xs)n)) 
                              (eqn0 = TT  P (nth(x : xs)n)) 
                              (n =   P (nth(x : xs)n)))"
(*   "!!x. P (test x) = (~ (∃a b. x = (a, b) & ~ P (test (a, b))))" *)
apply (cases n, simp)
apply (cases "n = 0", simp_all add: zero_Integer_def)
done



lemma nth_Cons_numeral [simp]:
  "(x : xs) !! 0 = x"
  "(x : xs) !! 1 = xs !! 0"
  "(x : xs) !! numeral (Num.Bit0 k) = xs !! numeral (Num.BitM k)"
  "(x : xs) !! numeral (Num.Bit1 k) = xs !! numeral (Num.Bit0 k)"
  by (simp_all add: nth_Cons numeral_Integer_eq
    zero_Integer_def one_Integer_def)

lemma take_strict [simp]:
  "takexs = "
  by (fixrec_simp)

lemma take_strict_2 [simp]:
  "le1i = TT  takei = "
  by (subst take.simps, cases "lei0") (auto dest: le_trans)

lemma drop_strict [simp]:
  "dropxs = "
  by (fixrec_simp)

lemma isPrefixOf_strict [simp]:
  "isPrefixOfxs = "
  "isPrefixOf(x:xs) = "
  by (fixrec_simp)+

lemma last_strict[simp]:
  "last= "
  "last(x:) = "
  by (fixrec_simp+)

lemma last_nil [simp]:
  "last[] = "
  by (fixrec_simp)

lemma last_spine_strict: "¬ finite_list xs  lastxs = "
proof (induct xs)
  case (Cons a xs)
  then show ?case by (cases xs) auto
qed auto

lemma init_strict [simp]:
  "init= "
  "init(x:) = "
  by (fixrec_simp+)

lemma init_nil [simp]:
  "init[] = "
  by (fixrec_simp)

lemma strict_foldr_strict2 [simp]:
  "(x. fx = )  foldrfxs = "
  by (induct xs, auto) fixrec_simp

lemma foldr_strict [simp]:
  "foldrfd = "
  "foldrf[] = "
  "foldrd(x : xs) = "
  by fixrec_simp+

lemma foldr_Cons_Nil [simp]:
  "foldr(:)[]xs = xs"
  by (induct xs) simp+

lemma append_strict1 [simp]:
  " ++ ys = "
  by fixrec_simp

lemma foldr_append [simp]:
  "foldrfa(xs ++ ys) = foldrf(foldrfays)xs"
  by (induct xs) simp+

lemma foldl_strict [simp]:
  "foldlfd = "
  "foldlf[] = "
  by fixrec_simp+

lemma foldr1_strict [simp]:
  "foldr1f= "
  "foldr1f(x:)= "
  by fixrec_simp+

lemma foldl1_strict [simp]:
  "foldl1f= "
  by fixrec_simp

lemma foldl_spine_strict:
  "¬ finite_list xs  foldlfxxs = "
  by (induct xs arbitrary: x) auto

lemma foldl_assoc_foldr:
  assumes "finite_list xs"
    and assoc: "x y z. f(fxy)z = fx(fyz)"
    and neutr1: "x. fzx = x"
    and neutr2: "x. fxz = x"
  shows "foldlfzxs = foldrfzxs"
  using ‹finite_list xs
proof (induct xs)
  case (Cons x xs)
  from ‹finite_list xs have step: "y. fy(foldlfzxs) = foldlf(fzy)xs"
  proof (induct xs)
    case (Cons x xs y)
    have "fy(foldlfz(x : xs)) = fy(foldlf(fzx)xs)" by auto
    also have "... = fy(fx(foldlfzxs))" by (simp only: Cons.hyps)
    also have "... = f(fyx)(foldlfzxs)" by (simp only: assoc)
    also have "... = foldlf(fz(fyx))xs" by (simp only: Cons.hyps)
    also have "... = foldlf(f(fzy)x)xs" by (simp only: assoc)
    also have "... = foldlf(fzy)(x : xs)" by auto
    finally show ?case.
  qed (simp add: neutr1 neutr2)

  have "foldlfz(x : xs) = foldlf(fzx)xs" by auto
  also have "... = fx(foldlfzxs)" by (simp only: step)
  also have "... = fx(foldrfzxs)" by (simp only: Cons.hyps)
  also have "... = (foldrfz(x:xs))" by auto
  finally show ?case .
qed auto

lemma elem_strict [simp]:
  "elemx = "
  by fixrec_simp

lemma notElem_strict [simp]:
  "notElemx = "
  by fixrec_simp

lemma list_eq_nil[simp]:
  "eql[] = TT  l = []"
  "eq[]l = TT  l = []"
  by (cases l, auto)+

lemma take_Nil [simp]:
  "n    taken[] = []"
  by (subst take.simps) (cases "len0"; simp)

lemma take_0 [simp]:
  "take0xs = []"
  "take(MkI0)xs = []"
  by (subst take.simps, simp add: zero_Integer_def)+

lemma take_Cons [simp]:
  "le1i = TT  takei(x:xs) = x : take(i - 1)xs"
  by (subst take.simps, cases "lei0") (auto dest: le_trans)

lemma take_MkI_Cons [simp]:
  "0 < n  take(MkIn)(x : xs) = x : take(MkI(n - 1))xs"
  by (subst take.simps) (simp add: zero_Integer_def one_Integer_def)

lemma take_numeral_Cons [simp]:
  "take1(x : xs) = [x]"
  "take(numeral (Num.Bit0 k))(x : xs) = x : take(numeral (Num.BitM k))xs"
  "take(numeral (Num.Bit1 k))(x : xs) = x : take(numeral (Num.Bit0 k))xs"
  by (subst take.simps,
      simp add: zero_Integer_def one_Integer_def numeral_Integer_eq)+

lemma drop_0 [simp]:
  "drop0xs = xs"
  "drop(MkI0)xs = xs"
  by (subst drop.simps, simp add: zero_Integer_def)+

lemma drop_pos [simp]:
  "len0 = FF  dropnxs = (case xs of []  [] | y : ys  drop(n - 1)ys)"
  by (subst drop.simps, simp)

lemma drop_numeral_Cons [simp]:
  "drop1(x : xs) = xs"
  "drop(numeral (Num.Bit0 k))(x : xs) = drop(numeral (Num.BitM k))xs"
  "drop(numeral (Num.Bit1 k))(x : xs) = drop(numeral (Num.Bit0 k))xs"
  by (subst drop.simps,
      simp add: zero_Integer_def one_Integer_def numeral_Integer_eq)+

lemma take_drop_append:
  "take(MkIi)xs ++ drop(MkIi)xs = xs"
proof (cases i)
  case (nonneg n)
  then show ?thesis
  proof (induct n arbitrary : i xs)
    case (Suc n)
    thus ?case
      apply (subst drop.simps)
      apply (subst take.simps)
      apply (cases xs)
        apply (auto simp add: zero_Integer_def one_Integer_def )
      done
  qed simp
next
  case (neg nat)
  then show ?thesis
    apply (subst drop.simps)
    apply (subst take.simps)
    apply (auto simp add: zero_Integer_def one_Integer_def )
    done
qed

lemma take_intsFrom_enumFrom [simp]:
  "take(MkIn)[MkIi..] = [MkIi..MkI(n+i) - 1]"
proof (cases n)
  fix m
  assume "n = int m"
  then show ?thesis
  proof (induct m arbitrary: n i)
    case 0 then show ?case by (simp add: one_Integer_def)
  next
    case (Suc m)
    then have "n - 1 = int m" by simp
    from Suc(1) [OF this]
    have "take(MkI(n - 1))[MkI(i+1)..] = [MkI(i+1)..MkI(n - 1 + (i+1)) - 1]" .
    moreover have "(n - 1) + (i+1) - 1 = n + i - 1" by arith
    ultimately have IH: "take(MkI(n - 1))[MkI(i+1)..] = [MkI(i+1)..MkI(n+i) - 1]" by simp
    from Suc(2) have gt: "n > 0" by simp
    moreover have "[MkIi..] = MkIi : [MkIi + 1..]" by (simp, subst intsFrom.simps) simp
    ultimately
    have *: "take(MkIn)[MkIi..] = MkIi : take(MkI(n - 1))[MkI(i+1)..]"
      by (simp add: one_Integer_def)
    show ?case unfolding IH * using gt by (simp add: one_Integer_def)
  qed
next
  fix m
  assume "n = - int m"
  then have "n  0" by simp
  then show ?thesis
    by (subst take.simps) (simp add: zero_Integer_def one_Integer_def)
qed

lemma drop_intsFrom_enumFrom [simp]:
  assumes "n  0"
  shows "drop(MkIn)[MkIi..] = [MkI(n+i)..]"
proof-
  from assms obtain n' where "n = int n'" by (cases n, auto)
  then show ?thesis
    apply(induct n' arbitrary: n i )
     apply simp
    apply (subst intsFrom.simps[unfolded enumFrom_intsFrom_conv[symmetric]])
    apply (subst drop.simps)
    apply (auto simp add: zero_Integer_def one_Integer_def)
    apply (rule cfun_arg_cong)
    apply (rule cfun_arg_cong)
    apply arith
    done
qed

lemma last_append_singleton:
  "finite_list xs  last(xs ++ [x]) = x"
proof (induct xs rule:finite_list.induct)
  case (Cons x xs)
  then show ?case by (cases xs) auto
qed auto

lemma init_append_singleton:
  "finite_list xs  init(xs ++ [x]) = xs"
proof (induct xs rule:finite_list.induct)
  case (Cons x xs)
  then show ?case by (cases xs) auto
qed auto

lemma append_Nil2 [simp]:
  "xs ++ [] = xs"
  by (induct xs) simp_all

lemma append_assoc [simp]:
  "(xs ++ ys) ++ zs = xs ++ ys ++ zs"
  by (induct xs) simp_all

lemma concat_simps [simp]:
  "concat[] = []"
  "concat(xs : xss) = xs ++ concatxss"
  "concat = "
  unfolding concat_def by simp_all

lemma concatMap_simps [simp]:
  "concatMapf[] = []"
  "concatMapf(x : xs) = fx ++ concatMapfxs"
  "concatMapf = "
  unfolding concatMap_def by simp_all

lemma filter_append [simp]:
  "filterP(xs ++ ys) = filterPxs ++ filterPys"
proof (induct xs)
  case (Cons x xs) then show ?case by (cases "Px") (auto simp: If_and_if)
qed simp_all

lemma elem_append [simp]:
  "elemx(xs ++ ys) = (elemxxs orelse elemxys)"
    by (induct xs) auto

lemma filter_filter [simp]:
  "filterP(filterQxs) = filter(Λ x. Qx andalso Px)xs"
  by (induct xs) (auto simp: If2_def [symmetric] split: split_If2)

lemma filter_const_TT [simp]:
  "filter(Λ _. TT)xs = xs"
  by (induct xs) simp_all

lemma tails_strict [simp]:
  "tails = "
  by fixrec_simp

lemma inits_strict [simp]:
  "inits = "
  by fixrec_simp

lemma the_and_strict [simp]:
  "the_and = "
  by fixrec_simp

lemma the_or_strict [simp]:
  "the_or = "
  by fixrec_simp

lemma all_strict [simp]:
  "allP = "
  by fixrec_simp

lemma any_strict [simp]:
  "anyP = "
  by fixrec_simp

lemma tails_neq_Nil [simp]:
  "tailsxs  []"
  by (cases xs) simp_all

lemma inits_neq_Nil [simp]:
  "initsxs  []"
  by (cases xs) simp_all

lemma Nil_neq_tails [simp]:
  "[]  tailsxs"
  by (cases xs) simp_all

lemma Nil_neq_inits [simp]:
  "[]  initsxs"
  by (cases xs) simp_all

lemma finite_list_not_bottom [simp]:
  assumes "finite_list xs" shows "xs  "
  using assms by (cases) simp_all

lemma head_append [simp]:
  "head(xs ++ ys) = If nullxs then headys else headxs"
  by (cases xs) simp_all

lemma filter_cong:
  "xset xs. px = qx  filterpxs = filterqxs"
proof (induct arbitrary: xs rule: filter.induct)
  case (3 x)
  then show ?case by (cases xs) auto
qed simp_all

lemma filter_TT [simp]:
  assumes "xset xs. Px = TT"
  shows "filterPxs = xs"
  by (rule filter_cong [of xs P "Λ _. TT", simplified, OF assms])

lemma filter_FF [simp]:
  assumes "finite_list xs"
    and "xset xs. Px = FF"
  shows "filterPxs = []"
  using assms by (induct xs) simp_all

lemma map_cong:
  "xset xs. px = qx  mappxs = mapqxs"
proof (induct arbitrary: xs rule: map.induct)
  case (3 x)
  then show ?case by (cases xs) auto
qed simp_all

lemma finite_list_upto:
  "finite_list (upto(MkIm)(MkIn))" (is "?P m n")
proof (cases "n - m")
  fix d
  assume "n - m = int d"
  then show "?P m n"
  proof (induct d arbitrary: m n)
    case (Suc d)
    then have "n - (m + 1) = int d" and le: "m  n" by simp_all
    from Suc(1) [OF this(1)] have IH: "?P (m+1) n" .
    then show ?case using le by (simp add: one_Integer_def)
  qed (simp add: one_Integer_def)
next
  fix d
  assume "n - m = - int d"
  then have "n  m" by auto
  moreover
  { assume "n = m" then have "?P m n" by (simp add: one_Integer_def) }
  moreover
  { assume "n < m" then have "?P m n" by (simp add: one_Integer_def) }
  ultimately show ?thesis by arith
qed

lemma filter_commute:
  assumes "xset xs. (Qx andalso Px) = (Px andalso Qx)"
  shows "filterP(filterQxs) = filterQ(filterPxs)"
  using filter_cong [of xs "Λ x. Qx andalso Px" "Λ x. Px andalso Qx"]
    and assms by simp

lemma upto_append_intsFrom [simp]:
  assumes "m  n"
  shows "upto(MkIm)(MkIn) ++ intsFrom(MkIn+1) = intsFrom(MkIm)"
    (is "?u m n ++ _ = ?i m")
proof (cases "n - m")
  case (nonneg i)
  with assms show ?thesis
  proof (induct i arbitrary: m n)
    case (Suc i)
    then have "m + 1  n" and "n - (m + 1) = int i" by simp_all
    from Suc(1) [OF this]
    have IH: "?u (m+1) n ++ ?i (n+1) = ?i (m+1)" by (simp add: one_Integer_def)
    from m + 1  n have "m  n" by simp
    then have "?u m n ++ ?i (n+1) = (MkIm : ?u (m+1) n) ++ ?i (n+1)"
      by (simp add: one_Integer_def)
    also have " = MkIm : ?i (m+1)" by (simp add: IH)
    finally show ?case by (subst (2) intsFrom.simps) (simp add: one_Integer_def)
  qed (subst (2) intsFrom.simps, simp add: one_Integer_def)
next
  case (neg i)
  then have "n < m" by simp
  with assms show ?thesis by simp
qed

lemma set_upto [simp]:
  "set (upto(MkIm)(MkIn)) = {MkIi | i. m  i  i  n}"
  (is "set (?u m n) = ?R m n")
proof (cases "n - m")
  case (nonneg i)
  then show ?thesis
  proof (induct i arbitrary: m n)
    case (Suc i)
    then have *: "n - (m + 1) = int i" by simp
    from Suc(1) [OF *] have IH: "set (?u (m+1) n) = ?R (m+1) n" .
    from * have "m  n" by simp
    then have "set (?u m n) = set (MkIm : ?u (m+1) n)" by (simp add: one_Integer_def)
    also have " = insert (MkIm) (?R (m+1) n)" by (simp add: IH)
    also have " = ?R m n" using m  n by auto
    finally show ?case .
  qed (force simp: one_Integer_def)
qed simp

lemma Nil_append_iff [iff]:
  "xs ++ ys = []  xs = []  ys = []"
  by (induct xs) simp_all

text ‹This version of definedness rule for Nil is made necessary by
the reorient simproc.›

lemma bottom_neq_Nil [simp]: "  []"
  by simp

text ‹Simproc to rewrite @{term "Nil = x"} to @{term "x = Nil"}.›

setup Reorient_Proc.add
    (fn Const(@{const_name Nil}, _) => true | _ => false)

simproc_setup reorient_Nil ("Nil = x") = Reorient_Proc.proc


lemma set_append [simp]:
  assumes "finite_list xs"
  shows "set (xs ++ ys) = set xs  set ys"
  using assms by (induct) simp_all

lemma distinct_Cons [simp]:
  "distinct (x : xs)  distinct xs  x  set xs"
  (is "?l = ?r")
proof
  assume ?l then show ?r by (cases) simp_all
next
  assume ?r then show ?l by auto
qed

lemma finite_list_append [iff]:
  "finite_list (xs ++ ys)  finite_list xs  finite_list ys"
  (is "?l = ?r")
proof
  presume "finite_list xs" and "finite_list ys"
  then show ?l by (induct) simp_all
next
  assume "?l" then show "?r"
  proof (induct "xs ++ ys" arbitrary: xs ys)
    case (Cons x xs)
    then show ?case by (cases xs) auto
  qed simp
qed simp_all

lemma distinct_append [simp]:
  assumes "finite_list (xs ++ ys)"
  shows "distinct (xs ++ ys)  distinct xs  distinct ys  set xs  set ys = {}"
    (is "?P xs ys")
  using assms
proof (induct "xs ++ ys" arbitrary: xs ys)
  case (Cons z zs)
  show ?case
  proof (cases xs)
    note Cons' = Cons
    case (Cons u us)
    with Cons' have "finite_list us"
      and [simp]: "zs = us ++ ys" "?P us ys" by simp_all
    then show ?thesis by (auto simp: Cons)
  qed (insert Cons, simp_all)
qed simp

lemma finite_set [simp]:
  assumes "distinct xs"
  shows "finite (set xs)"
  using assms by induct auto

lemma distinct_card:
  assumes "distinct xs"
  shows "MkI(int (card (set xs))) = lengthxs"
  using assms
  by (induct)
     (simp_all add: zero_Integer_def plus_MkI_MkI [symmetric] one_Integer_def ac_simps)

lemma set_delete [simp]:
  fixes xs :: "['a::Eq_eq]"
  assumes "distinct xs"
    and "xset xs. eqax  "
  shows "set (deleteaxs) = set xs - {a}"
  using assms
proof induct
  case (Cons x xs)
  then show ?case by (cases "eqax", force+)
qed simp

lemma distinct_delete [simp]:
  fixes xs :: "['a::Eq_eq]"
  assumes "distinct xs"
    and "xset xs. eqax  "
  shows "distinct (deleteaxs)"
  using assms
proof induct
  case (Cons x xs)
  then show ?case by (cases "eqax", force+)
qed simp

lemma set_diff [simp]:
  fixes xs ys :: "['a::Eq_eq]"
  assumes "distinct ys" and "distinct xs"
    and "aset ys. xset xs. eqax  "
  shows "set (xs \\\\ ys) = set xs - set ys"
  using assms
proof (induct arbitrary: xs)
  case Nil then show ?case by (induct xs rule: distinct.induct) simp_all
next
  case (Cons y ys)
  let ?xs = "deleteyxs"
  from Cons have *: "xset xs. eqyx  " by simp
  from set_delete [OF ‹distinct xs this]
  have **: "set ?xs = set xs - {y}" .
  with Cons have "aset ys. xset ?xs. eqax  " by simp
  moreover from * and ‹distinct xs have "distinct ?xs" by simp
  ultimately have "set (?xs \\\\ ys) = set ?xs - set ys"
    using Cons by blast
  then show ?case by (force simp: **)
qed

lemma distinct_delete_filter:
  fixes xs :: "['a::Eq_eq]"
  assumes "distinct xs"
    and "xset xs. eqax  "
  shows "deleteaxs = filter(Λ x. neqax)xs"
  using assms
proof (induct)
  case (Cons x xs)
  then have IH: "deleteaxs = filter(Λ x. neqax)xs" by simp
  show ?case
  proof (cases "eqax")
    case TT
    have "xset xs. (Λ x. neqax)x = (Λ _. TT)x"
    proof
      fix y
      assume "y  set xs"
      with Cons(3, 4) have "x  y" and "eqay  " by auto
      with TT have "eqay = FF" by (metis (no_types) eqD(1) trE)
      then show "(Λ x. neqax)y = (Λ _. TT)y" by simp
    qed
    from filter_cong [OF this] and TT
    show ?thesis by simp
  qed (simp_all add: IH)
qed simp

lemma distinct_diff_filter:
  fixes xs ys :: "['a::Eq_eq]"
  assumes "finite_list ys"
    and "distinct xs"
    and "aset ys. xset xs. eqax  "
  shows "xs \\\\ ys = filter(Λ x. neg(elemxys))xs"
  using assms
proof (induct arbitrary: xs)
  case Nil then show ?case by simp
next
  case (Cons y ys)
  let ?xs = "deleteyxs"
  from Cons have *: "xset xs. eqyx  " by simp
  from set_delete [OF ‹distinct xs this]
  have "set ?xs = set xs - {y}" .
  with Cons have "aset ys. xset ?xs. eqax  " by simp
  moreover from * and ‹distinct xs have "distinct ?xs" by simp
  ultimately have "?xs \\\\ ys = filter(Λ x. neg(elemxys))?xs"
    using Cons by blast
  then show ?case
    using ‹distinct xs and *
    by (simp add: distinct_delete_filter)
qed

lemma distinct_upto [intro, simp]:
  "distinct [MkIm..MkIn]"
proof (cases "n - m")
  case (nonneg i)
  then show ?thesis
  proof (induct i arbitrary: m)
    case (Suc i)
    then have "n - (m + 1) = int i" and "m  n" by simp_all
    with Suc have "distinct [MkI(m+1)..MkIn]" by force
    with m  n show ?case by (simp add: one_Integer_def)
  qed (simp add: one_Integer_def)
qed simp

lemma set_intsFrom [simp]:
  "set (intsFrom(MkIm)) = {MkIn | n. m  n}"
  (is "set (?i m) = ?I")
proof
  show "set (?i m)  ?I"
  proof
    fix n
    assume "n  set (?i m)"
    then have "listmem n (?i m)" by (simp add: set_def)
    then show "n  ?I"
    proof (induct n "(?i m)" arbitrary: m)
      fix x xs m
      assume "x : xs = ?i m"
      then have "x : xs = MkIm : ?i (m+1)"
        by (subst (asm) intsFrom.simps) (simp add: one_Integer_def)
      then have [simp]: "x = MkIm" "xs = ?i (m+1)" by simp_all
      show "x  {MkIn | n. m  n}" by simp
    next
      fix x xs y m
      assume IH: "listmem x xs"
        "m. xs = ?i m  x  {MkIn | n. m  n}"
        "y : xs = ?i m"
      then have "y : xs = MkIm : ?i (m+1)"
        by (subst (asm) (2) intsFrom.simps) (simp add: one_Integer_def)
      then have [simp]: "y = MkIm" "xs = ?i (m+1)" by simp_all
      from IH (2) [of "m+1"] have "x  {MkIn | n. m+1  n}" by (simp add: one_Integer_def)
      then show "x  {MkIn | n. m  n}" by force
    qed
  qed
next
  show "?I  set (?i m)"
  proof
    fix x
    assume "x  ?I"
    then obtain n where [simp]: "x = MkIn" and "m  n" by blast
    from upto_append_intsFrom [OF this(2), symmetric]
    have *: "set (?i m) = set (upto(MkIm)(MkIn))  set (?i (n+1))"
      using finite_list_upto [of m n] by (simp add: one_Integer_def)
    show "x  set (?i m)" using m  n by (auto simp: * one_Integer_def)
  qed
qed

lemma If_eq_bottom_iff [simp]: (* FIXME: move *)
  "(If b then x else y = )  b =   b = TT  x =   b = FF  y = "
  by (induct b) simp_all

lemma upto_eq_bottom_iff [simp]:
  "uptomn =   m =   n = "
  by (subst upto.simps, simp)

lemma seq_eq_bottom_iff [simp]: (* FIXME: move *)
  "seqxy =   x =   y = "
  by (simp add: seq_conv_if)

lemma intsFrom_eq_bottom_iff [simp]:
  "intsFromm =   m = "
  by (subst intsFrom.simps, simp)

lemma intsFrom_split:
  assumes "m  n"
  shows "[MkIn..] = [MkIn .. MkI(m - 1)] ++ [MkIm..]"
proof-
  from assms have ge: "m - n  0" by arith
  have "[MkIn..] = (take(MkI(m - n))  [MkIn..]) ++ (drop(MkI(m - n))  [MkIn..])"
    by (subst take_drop_append, rule)
  also have "... = [MkIn.. MkI(m - 1)] ++ [MkIm..]"
    by (subst drop_intsFrom_enumFrom[OF ge], auto simp add:take_intsFrom_enumFrom[simplified] one_Integer_def)
  finally show ?thesis .
qed

lemma filter_fast_forward:
  assumes "n+1  n'"
    and  "k . n < k  k < n'  ¬ P k"
  shows "filter(Λ (MkIi) . Def (P i))[MkI(n+1)..] = filter(Λ (MkIi) . Def (P i))[MkIn'..]"
proof-
  from assms(1)
  have"[MkI(n+1)..] = [MkI(n+1).. MkI(n'- 1)] ++ [MkIn'..]" (is "_ = ?l1 ++ ?l2")
    by (subst intsFrom_split[of "n+1" n'], auto)
  moreover
  have "filter(Λ (MkIi) . Def (P i))[MkI(n+1).. MkI(n'- 1)] = []"
    apply (rule filter_FF)
     apply (simp, rule finite_list_upto)
    using assms(2)
    apply auto
    done
  ultimately
  show ?thesis by simp
qed

lemma null_eq_TT_iff [simp]:
  "nullxs = TT  xs = []"
  by (cases xs) simp_all

lemma null_set_empty_conv:
  "xs    nullxs = TT  set xs = {}"
  by (cases xs) simp_all

lemma elem_TT [simp]:
  fixes x :: "'a::Eq_eq" shows "elemxxs = TT  x  set xs"
  apply (induct arbitrary: xs rule: elem.induct, simp_all)
  apply (rename_tac xs, case_tac xs, simp_all)
  apply (rename_tac a list, case_tac "eqax", force+)
  done

lemma elem_FF [simp]:
  fixes x :: "'a::Eq_equiv" shows "elemxxs = FF  x  set xs"
  by (induct arbitrary: xs rule: elem.induct, simp_all)
     (rename_tac xs, case_tac xs, simp_all, force)

lemma length_strict [simp]:
  "length = "
  by (fixrec_simp)

lemma repeat_neq_bottom [simp]:
  "repeatx  "
  by (subst repeat.simps) simp

lemma list_case_repeat [simp]:
  "list_caseaf(repeatx) = fx(repeatx)"
  by (subst repeat.simps) simp

lemma length_append [simp]:
  "length(xs ++ ys) = lengthxs + lengthys"
  by (induct xs) (simp_all add: ac_simps)

lemma replicate_strict [simp]:
  "replicatex = "
  by (simp add: replicate_def)

lemma replicate_0 [simp]:
  "replicate0x = []"
  "replicate(MkI0)xs = []"
  by (simp add: replicate_def)+

lemma Integer_add_0 [simp]: "MkI0 + n = n"
  by (cases n) simp_all

lemma replicate_MkI_plus_1 [simp]:
  "0  n  replicate(MkI(n+1))x = x : replicate(MkIn)x"
  "0  n  replicate(MkI(1+n))x = x : replicate(MkIn)x"
  by (simp add: replicate_def, subst take.simps, simp add: one_Integer_def zero_Integer_def)+

lemma replicate_append_plus_conv:
  assumes "0  m" and "0  n"
  shows "replicate(MkIm)x ++ replicate(MkIn)x
    = replicate(MkIm + MkIn)x"
proof (cases m)
  case (nonneg i)
  with assms show ?thesis
  proof (induct i arbitrary: m)
    case (Suc i)
    then have ge: "int i + n  0" by force
    have "replicate(MkIm)x ++ replicate(MkIn)x = x : (replicate(MkI(int i))x ++ replicate(MkIn)x)" by (simp add: Suc)
    also have " = x : replicate(MkI(int i) + MkIn)x" using Suc by simp
    finally show ?case using ge by (simp add: Suc ac_simps)
  qed simp
next
  case (neg i)
  with assms show ?thesis by simp
qed

lemma replicate_MkI_1 [simp]:
  "replicate(MkI1)x = x : []"
  by (simp add: replicate_def, subst take.simps, simp add: zero_Integer_def one_Integer_def)

lemma length_replicate [simp]:
  assumes "0  n"
  shows "length(replicate(MkIn)x) = MkIn"
proof (cases n)
  case (nonneg i)
  with assms show ?thesis
    by (induct i arbitrary: n)
       (simp_all add: replicate_append_plus_conv zero_Integer_def one_Integer_def)
next
  case (neg i)
  with assms show ?thesis by (simp add: replicate_def)
qed

lemma map_oo [simp]:
  "mapf(mapgxs) = map(f oo g)xs"
  by (induct xs) simp_all

lemma nth_Cons_MkI [simp]:
  "0 < i  (a : xs) !! (MkIi) = xs !! (MkI(i - 1))"
  unfolding nth_Cons
  by (cases i, simp add: zero_Integer_def one_Integer_def) (case_tac n, simp_all)

lemma map_plus_intsFrom:
  "map(+ MkIn)(intsFrom(MkIm)) = intsFrom(MkI(m+n))" (is "?l = ?r")
proof (rule list.take_lemma)
  fix i show "list_take i?l = list_take i?r"
  proof (induct i arbitrary: m)
    case (Suc i) then show ?case
      by (subst (1 2) intsFrom.simps) (simp add: ac_simps one_Integer_def)
  qed simp
qed

lemma plus_eq_MkI_conv:
  "l + n = MkIm  (l' n'. l = MkIl'  n = MkIn'  m = l' + n')"
  by (cases l, simp) (cases n, auto)

lemma length_ge_0:
  "lengthxs = MkIn  n  0"
  by (induct xs arbitrary: n) (auto simp: one_Integer_def plus_eq_MkI_conv)

lemma length_0_conv [simp]:
  "lengthxs = MkI0  xs = []"
  apply (cases xs)
    apply (simp_all add: one_Integer_def)
  apply (case_tac "lengthlist")
   apply (auto dest: length_ge_0)
  done

lemma length_ge_1 [simp]:
  "lengthxs = MkI(1 + int n)
     (u us. xs = u : us  lengthus = MkI(int n))"
  (is "?l = ?r")
proof
  assume ?r then show ?l by (auto simp: one_Integer_def)
next
  assume 1: ?l
  then obtain u us where [simp]: "xs = u : us" by (cases xs) auto
  from 1 have 2: "1 + lengthus = MkI(1 + int n)" by (simp add: ac_simps)
  then have "lengthus  " by (cases "lengthus") simp_all
  moreover from 2 have "lengthus + 1 = MkI(int n) + 1" by (simp add: one_Integer_def ac_simps)
  ultimately have "lengthus = MkI(int n)"
    by (cases "lengthus") (simp_all add: one_Integer_def)
  then show ?r by simp
qed

lemma finite_list_length_conv:
  "finite_list xs  (n. lengthxs = MkI(int n))" (is "?l = ?r")
proof
  assume "?l" then show "?r"
    by (induct, auto simp: one_Integer_def) presburger
next
  assume "?r"
  then obtain n where "lengthxs = MkI(int n)" by blast
  then show "?l" by (induct n arbitrary: xs) auto
qed

lemma nth_append:
  assumes "lengthxs = MkIn" and "n  m"
  shows "(xs ++ ys) !! MkIm = ys !! MkI(m - n)"
  using assms
proof (induct xs arbitrary: n m)
  case (Cons x xs)
  then have ge: "n  0" by (blast intro: length_ge_0)
  from Cons(2)
  have len: "lengthxs = MkI(n - 1)"
    by (auto simp: plus_eq_MkI_conv one_Integer_def)
  from Cons(3) have le: "n - 1  m - 1" by simp
  { assume "m < 0"
    with ge have ?case using Cons(3) by simp }
  moreover
  { assume "m = 0"
    with Cons(3) and ge have "n = 0" by simp
    with Cons(2) have ?case
      by (auto dest: length_ge_0 simp: one_Integer_def plus_eq_MkI_conv) }
  moreover
  { assume "m > 0"
    then have ?case
      by (auto simp: Cons(1) [OF len le] zero_Integer_def one_Integer_def) }
  ultimately show ?case by arith
qed (simp_all add: zero_Integer_def)

lemma replicate_nth [simp]:
  assumes "0  n"
  shows "(replicate(MkIn)x ++ xs) !! MkIn = xs !! MkI0"
  using nth_append [OF length_replicate [OF assms], of n]
    by simp

lemma map2_zip:
  "map(Λx, y. x, fy)(zipxsys) = zipxs(mapfys)"
  by (induct xs arbitrary: ys) (simp_all, case_tac ys, simp_all)

lemma map2_filter:
  "map(Λx, y. x, fy)(filter(Λx, y. Px)xs)
    = filter(Λx, y. Px)(map(Λx, y. x, fy)xs)"
  apply (induct xs, simp_all)
  apply (rename_tac x xs, case_tac x, simp, simp)
  apply (rename_tac a b, case_tac "Pa", auto)
done

lemma map_map_snd:
  "f =   mapf(mapsndxs)
    = mapsnd(map(Λx, y. x, fy)xs)"
  by (induct xs, simp_all, rename_tac a b, case_tac a, simp_all)

lemma findIndices_Cons [simp]:
  "findIndicesP(a : xs) =
    If Pa then 0 : map(+1)(findIndicesPxs)
    else map(+1)(findIndicesPxs)"
  by (auto simp: findIndices_def, subst intsFrom.simps, cases "Pa")
     (simp_all
       del: map_oo
       add: map_oo [symmetric] map_map_snd one_Integer_def zero_Integer_def
       map_plus_intsFrom [of 1 0, simplified, symmetric]
       map2_zip [of "(+ MkI1)", simplified]
       map2_filter [of "(+ MkI1)", simplified])

lemma filter_alt_def:
  fixes xs :: "['a]"
  shows "filterPxs = map(nthxs)(findIndicesPxs)"
proof -
  {
    fix f g :: "Integer  'a"
      and P :: "'a  tr"
      and i xs
    assume "ji. f(MkIj) = g(MkIj)"
    then have "mapf(mapsnd(filter(Λx, i. Px)(zipxs[MkIi..])))
      = mapg(mapsnd(filter(Λx, i. Px)(zipxs[MkIi..])))"
      by (induct xs arbitrary: i, simp_all, subst (1 2) intsFrom.simps)
        (rename_tac a b c, case_tac "Pa", simp_all add: one_Integer_def)
  } note 1 = this
  {
    fix a and ys :: "['a]"
    have "i0. nthys(MkIi) = (nth(a : ys) oo (+1))(MkIi)"
      by (auto simp: one_Integer_def zero_Integer_def)
  } note 2 = this
  {
    fix a P and ys xs :: "['a]"
    have "map(nth(a : ys) oo (+1))(findIndicesPxs)
      = map(nthys)(findIndicesPxs)"
      by (simp add: findIndices_def 1 [OF 2, simplified, of ys P xs a] zero_Integer_def)
  } note 3 = this
  show ?thesis
    by (induct xs, simp_all, simp add: findIndices_def, simp add: findIndices_def)
       (rename_tac a b, case_tac "Pa", simp add: findIndices_def, simp_all add: 3)
qed

abbreviation cfun_image :: "('a  'b)  'a set  'b set" (infixr "`⋅" 90) where
  "f `⋅ A  Rep_cfun f ` A"

lemma set_map:
  "set (mapfxs) = f `⋅ set xs" (is "?l = ?r")
proof
  { fix a assume "listmem a xs" then have "listmem (fa) (mapfxs)"
      by (induct) simp_all }
  then show "?r  ?l" by (auto simp: set_def)
next
  { fix a assume "listmem a (mapfxs)"
    then have "b. a = fb  listmem b xs"
    by (induct a "mapfxs" arbitrary: xs)
       (rename_tac xsa, case_tac xsa, auto)+ }
  then show "?l  ?r" unfolding set_def by auto
qed


subsection @{const reverse} and @{const reverse} induction›

text ‹Alternative simplification rules for @{const reverse} (easier
to use for equational reasoning):›
lemma reverse_Nil [simp]:
  "reverse[] = []"
  by (simp add: reverse.simps)

lemma reverse_singleton [simp]:
  "reverse[x] = [x]"
  by (simp add: reverse.simps)

lemma reverse_strict [simp]:
  "reverse = "
  by (simp add: reverse.simps)

lemma foldl_flip_Cons_append:
  "foldl(flip(:))ysxs = foldl(flip(:))[]xs ++ ys"
proof (induct xs arbitrary: ys)
  case (Cons x xs)
  show ?case by simp (metis (no_types) Cons append.simps append_assoc)
qed simp+

lemma reverse_Cons [simp]:
  "reverse(x:xs) = reversexs ++ [x]"
  by (simp add: reverse.simps)
     (subst foldl_flip_Cons_append, rule refl)

lemma reverse_append_below:
  "reverse(xs ++ ys)  reverseys ++ reversexs"
  apply (induction xs)
     apply (simp del: append_assoc add: append_assoc [symmetric])+
  apply (blast intro: monofun_cfun append_assoc)
  done

lemma reverse_reverse_below:
  "reverse(reversexs)  xs"
proof (induction xs)
  case (Cons x xs)
  have "reverse(reverse(x:xs)) = reverse(reversexs ++ [x])" by simp
  also have "  reverse[x] ++ reverse(reversexs)" by (rule reverse_append_below)
  also have " = x : reverse(reversexs)" by simp
  also have "  x : xs" by (simp add: Cons)
  finally show ?case .
qed simp+

lemma reverse_append [simp]:
  assumes "finite_list xs"
  shows "reverse(xs ++ ys) = reverseys ++ reversexs"
  using assms by (induct xs) simp+

lemma reverse_spine_strict:
  "¬ finite_list xs  reversexs = "
  by (auto simp add: reverse.simps foldl_spine_strict)

lemma reverse_finite [simp]:
  assumes "finite_list xs" shows "finite_list (reversexs)"
  using assms by (induct xs) simp+

lemma reverse_reverse [simp]:
  assumes "finite_list xs" shows "reverse(reversexs) = xs"
  using assms by (induct xs) simp+

lemma reverse_induct [consumes 1, case_names Nil snoc]:
  "finite_list xs; P []; x xs . finite_list xs  P xs  P (xs ++ [x])  P xs"
  apply (subst reverse_reverse [symmetric])
   apply assumption
  apply (rule finite_list.induct[where x = "reversexs"])
    apply simp+
  done

lemma length_plus_not_0:
  "le1n = TT  le(lengthxs + n)0 = TT  False"
proof (induct xs arbitrary: n)
  case Nil then show ?case
    by auto (metis Ord_linear_class.le_trans dist_eq_tr(3) le_Integer_numeral_simps(3))
next
  case (Cons x xs)
  from Cons(1) [of "n + 1"] show ?case
    using Cons(2-) by (auto simp: ac_simps dest: le_plus_1)
qed simp+

lemma take_length_plus_1:
  "lengthxs    take(lengthxs + 1)(y:ys) = y : take(lengthxs)ys"
  by (subst take.simps, cases "le(lengthxs + 1)0")
     (auto, metis (no_types) length_plus_not_0 le_Integer_numeral_simps(4))

lemma le_length_plus:
  "lengthxs    n    len(lengthxs + n) = TT"
proof (induct xs arbitrary: n)
  case (Cons x xs)
  then have "le(n + 1)(lengthxs + (n + 1)) = TT" by simp
  moreover have "len(n + 1) = TT" using n   by (metis le_plus_1 le_refl_Integer)
  ultimately have "len(lengthxs + (n + 1)) = TT" by (blast dest: le_trans)
  then show ?case by (simp add: ac_simps)
qed simp+

lemma eq_take_length_isPrefixOf:
  "eqxs(take(lengthxs)ys)  isPrefixOfxsys"
proof (induct xs arbitrary: ys)
  case (Cons x xs)
  note IH = this
  show ?case
  proof (cases "lengthxs = ")
    case True then show ?thesis by simp
  next
    case False
    show ?thesis
    proof (cases ys)
      case bottom
      then show ?thesis using False
        using le_length_plus [of xs 1] by simp
    next
      case Nil then show ?thesis using False by simp
    next
      case (Cons z zs)
      then show ?thesis
        using False and IH [of zs]
        by (simp add: take_length_plus_1 monofun_cfun_arg)
    qed
  qed
qed simp+

end

Theory Data_Maybe

section ‹Data: Maybe›

theory Data_Maybe
  imports
    Type_Classes
    Data_Function
    Data_List
    Data_Bool
begin

domain 'a Maybe = Nothing | Just (lazy "'a")

abbreviation maybe :: "'b  ('a  'b)  'a Maybe  'b" where
  "maybe  Maybe_case"

fixrec isJust :: "'a Maybe  tr" where
  "isJust(Justa) = TT" |
  "isJustNothing = FF"

fixrec isNothing :: "'a Maybe  tr" where
  "isNothing = neg oo isJust"

fixrec fromJust :: "'a Maybe  'a" where
  "fromJust(Justa) = a" |
  "fromJustNothing = "

fixrec fromMaybe :: "'a  'a Maybe  'a" where
  "fromMaybedNothing = d" |
  "fromMaybed(Justa) = a"

fixrec maybeToList :: "'a Maybe  ['a]" where
  "maybeToListNothing = []" |
  "maybeToList(Justa) = [a]"

fixrec listToMaybe :: "['a]  'a Maybe" where
  "listToMaybe[] = Nothing" |
  "listToMaybe(a:_) = Justa"

(* FIXME: The Prelude definition uses list comps, which are too advanced for our syntax *)
fixrec catMaybes :: "['a Maybe]  ['a]" where
  "catMaybes = concatMapmaybeToList"

fixrec mapMaybe :: "('a  'b Maybe)  ['a]  ['b]" where
  "mapMaybef = catMaybes oo mapf"

instantiation Maybe :: (Eq) Eq_strict
begin

definition
  "eq = maybe(maybeTT(Λ y. FF))(Λ x. maybeFF(Λ y. eqxy))"

instance proof
  fix x :: "'a Maybe"
  show "eqx = "
    unfolding eq_Maybe_def by (cases x) simp_all
  show "eqx = "
    unfolding eq_Maybe_def by simp
qed

end

lemma eq_Maybe_simps [simp]:
  "eqNothingNothing = TT"
  "eqNothing(Justy) = FF"
  "eq(Justx)Nothing = FF"
  "eq(Justx)(Justy) = eqxy"
  unfolding eq_Maybe_def by simp_all

instance Maybe :: (Eq_sym) Eq_sym
proof
  fix x y :: "'a Maybe"
  show "eqxy = eqyx"
    by (cases x, simp, cases y, simp, simp, simp,
        cases y, simp, simp, simp add: eq_sym)
qed

instance Maybe :: (Eq_equiv) Eq_equiv
proof
  fix x y z :: "'a Maybe"
  show "eqxx  FF"
    by (cases x, simp_all)
  assume "eqxy = TT" and "eqyz = TT" then show "eqxz = TT"
    by (cases x, simp, cases y, simp, cases z, simp, simp, simp, simp,
        cases y, simp, simp, cases z, simp, simp, simp, fast elim: eq_trans)
qed

instance Maybe :: (Eq_eq) Eq_eq
proof
  fix x y :: "'a Maybe"
  show "eqxx  FF"
    by (cases x, simp_all)
  assume "eqxy = TT" then show "x = y"
    by (cases x, simp, cases y, simp, simp, simp,
        cases y, simp, simp, simp, fast)
qed

instantiation Maybe :: (Ord) Ord_strict
begin

definition
  "compare = maybe(maybeEQ(Λ y. LT))(Λ x. maybeGT(Λ y. comparexy))"

instance proof
  fix x :: "'a Maybe"
  show "comparex = "
    unfolding compare_Maybe_def by (cases x) simp_all
  show "comparex = "
    unfolding compare_Maybe_def by simp
qed

end

lemma compare_Maybe_simps [simp]:
  "compareNothingNothing = EQ"
  "compareNothing(Justy) = LT"
  "compare(Justx)Nothing = GT"
  "compare(Justx)(Justy) = comparexy"
  unfolding compare_Maybe_def by simp_all

instance Maybe :: (Ord_linear) Ord_linear
proof
  fix x y z :: "'a Maybe"
  show "eqxy = is_EQ(comparexy)"
    by (cases x, simp, cases y, simp, simp, simp,
        cases y, simp, simp, simp add: eq_conv_compare)
  show "oppOrdering(comparexy) = compareyx"
    by (cases x, simp, (cases y, simp, simp, simp)+)
  show "comparexx  EQ"
    by (cases x) simp_all
  { assume "comparexy = EQ" then show "x = y"
      by (cases x, simp, cases y, simp, simp, simp,
          cases y, simp, simp, simp) (erule compare_EQ_dest) }
  { assume "comparexy = LT" and "compareyz = LT" then show "comparexz = LT"
      apply (cases x, simp)
       apply (cases y, simp, simp)
       apply (cases z, simp, simp, simp)
      apply (cases y, simp, simp)
      apply (cases z, simp, simp)
      apply (auto elim: compare_LT_trans)
      done }
qed

lemma isJust_strict [simp]: "isJust  = " by (fixrec_simp)
lemma fromMaybe_strict [simp]: "fromMaybex  = " by (fixrec_simp)
lemma maybeToList_strict [simp]: "maybeToList  = " by (fixrec_simp)

end

Theory Definedness

section ‹Definedness›

theory Definedness
  imports
    Data_List
begin

text ‹
  This is an attempt for a setup for better handling bottom, by a better
  simp setup, and less breaking the abstractions.
›

definition defined :: "'a :: pcpo  bool" where
  "defined x = (x  )"

lemma defined_bottom [simp]: "¬ defined "
  by (simp add: defined_def)

lemma defined_seq [simp]: "defined x  seqxy = y"
  by (simp add: defined_def)

consts val :: "'a::type  'b::type" ("_")

text ‹val for booleans›

definition val_Bool :: "tr  bool" where
  "val_Bool i = (THE j. i = Def j)"

adhoc_overloading
  val val_Bool

lemma defined_Bool_simps [simp]:
  "defined (Def i)"
  "defined TT"
  "defined FF"
  by (simp_all add: defined_def)

lemma val_Bool_simp1 [simp]:
  "Def i = i"
  by (simp_all add: val_Bool_def TT_def FF_def)

lemma val_Bool_simp2 [simp]:
  "TT = True"
  "FF = False"
  by (simp_all add: TT_def FF_def)

lemma IF_simps [simp]:
  "defined b   b   (If b then x else y) = x"
  "defined b   b  = False  (If b then x else y) = y"
  by (cases b, simp_all)+

lemma defined_neg [simp]: "defined (negb)  defined b"
  by (cases b, auto)

lemma val_Bool_neg [simp]: "defined b   neg  b  = (¬  b )"
  by (cases b, auto)

text ‹val for integers›

definition val_Integer :: "Integer  int" where
  "val_Integer i = (THE j. i = MkIj)"

adhoc_overloading
  val val_Integer

lemma defined_Integer_simps [simp]:
  "defined (MkIi)"
  "defined (0::Integer)"
  "defined (1::Integer)"
  by (simp_all add: defined_def)

lemma defined_numeral [simp]: "defined (numeral x :: Integer)"
  by (simp add: defined_def)

lemma val_Integer_simps [simp]:
  "MkIi = i"
  "0 = 0"
  "1 = 1"
  by (simp_all add: val_Integer_def)

lemma val_Integer_numeral [simp]: " numeral x :: Integer  = numeral x"
  by (simp_all add: val_Integer_def)


lemma val_Integer_to_MkI:
  "defined i  i = (MkI   i )"
  apply (cases i)
   apply (auto simp add: val_Integer_def defined_def)
  done

lemma defined_Integer_minus [simp]: "defined i  defined j  defined (i - (j::Integer))"
  apply (cases i, auto)
  apply (cases j, auto)
  done

lemma val_Integer_minus [simp]: "defined i  defined j   i - j  =  i  -  j "
  apply (cases i, auto)
  apply (cases j, auto)
  done

lemma defined_Integer_plus [simp]: "defined i  defined j  defined (i + (j::Integer))"
  apply (cases i, auto)
  apply (cases j, auto)
  done

lemma val_Integer_plus [simp]: "defined i  defined j   i + j  =  i  +  j "
  apply (cases i, auto)
  apply (cases j, auto)
  done

lemma defined_Integer_eq [simp]: "defined (eqab)  defined a  defined (b::Integer)"
  apply (cases a, simp)
  apply (cases b, simp)
  apply simp
  done

lemma val_Integer_eq [simp]: "defined a  defined b   eqab  = ( a  = ( b  :: int))"
  apply (cases a, simp)
  apply (cases b, simp)
  apply simp
  done


text ‹Full induction for non-negative integers›

lemma nonneg_full_Int_induct [consumes 1, case_names neg Suc]:
  assumes defined: "defined i"
  assumes neg: " i. defined i  i < 0  P i"
  assumes step: " i. defined i  0  i  ( j. defined j   j  <  i   P j)  P i"
  shows "P (i::Integer)"
proof (cases i)
  case bottom
  then have False using defined by simp
  then show ?thesis ..
next
  case (MkI integer)
  show ?thesis
  proof (cases integer)
    case neg
    then show ?thesis using assms(2) MkI by simp
  next
    case (nonneg nat)
    have "P (MkI(int nat))"
    proof(induction nat rule:full_nat_induct)
      case (1 nat)
      have "defined (MkI(int nat))" by simp
      moreover
      have "0   MkI(int nat) "  by simp
      moreover
      { fix j::Integer
        assume "defined j" and le: " j  <  MkI(int nat) "
        have "P j"
        proof(cases j)
          case bottom with ‹defined j show ?thesis by simp
        next
          case (MkI integer)
          show ?thesis
          proof(cases integer)
            case (neg nat)
            have "j < 0" using neg MkI by simp
            with ‹defined j
            show ?thesis by (rule assms(2))
          next
            case (nonneg m)
            have "Suc m  nat" using le nonneg MkI by simp
            then have "P (MkI(int m))" by (metis "1.IH")
            then show ?thesis using nonneg MkI by simp
          qed
        qed
      }
      ultimately
      show ?case
        by (rule step)
    qed
    then show ?thesis using nonneg MkI by simp
  qed
qed

text ‹Some list lemmas re-done with the new setup.›

lemma nth_tail: (* TODO: move *)
  "defined n   n   0   tailxs !! n = xs !! (1 + n)"
  apply (cases xs, simp_all)
  apply (cases n, simp)
  apply (simp add: one_Integer_def zero_Integer_def)
  done

lemma nth_zipWith: (* TODO: move *)
  assumes f1 [simp]: "y. fy = "
  assumes f2 [simp]: "x. fx = "
  shows "zipWithfxsys !! n = f(xs !! n)(ys !! n)"
proof (induct xs arbitrary: ys n)
  case (Cons x xs ys n) then show ?case
    by (cases ys, simp_all split:nth_Cons_split)
qed simp_all


lemma nth_neg [simp]: "defined n   n  < 0  nthxsn = "
proof (induction xs arbitrary: n)
  have [simp]: "eqn0 = TT  (n::Integer) = 0" for n
    by (cases n, auto simp add: zero_Integer_def)
  case (Cons a xs n)
  have "eqn0 = FF"
    using Cons.prems
    by (cases "eqn0") auto
  then show ?case
    using Cons.prems
    by (auto intro: Cons.IH)
qed simp_all

lemma nth_Cons_simp [simp]:
  "defined n   n  = 0  nth(x : xs)n = x"
  "defined n   n  > 0  nth(x : xs)n = nthxs(n - 1)"
proof -
  assume "defined n" and " n  = 0"
  then have "n = 0"  by (cases n) auto
  then show "nth(x : xs)n = x" by simp
next
  assume "defined n" and " n  > 0"
  then have "eqn0 = FF" by (cases "eqn0") auto
  then show "nth(x : xs)n = nthxs(n - 1)" by simp
qed

end

Theory List_Comprehension

section ‹List Comprehension›

theory List_Comprehension
  imports Data_List
begin

no_notation
  disj (infixr "|" 30)

nonterminal llc_qual and llc_quals

syntax
  "_llc" :: "'a  llc_qual  llc_quals  ['a]" ("[_ | __")
  "_llc_gen" :: "'a  ['a]  llc_qual" ("_ <- _")
  "_llc_guard" :: "tr  llc_qual" ("_")
  "_llc_let" :: "letbinds  llc_qual" ("let _")
  "_llc_quals" :: "llc_qual  llc_quals  llc_quals" (", __")
  "_llc_end" :: "llc_quals" ("]")
  "_llc_abs" :: "'a  ['a]  ['a]"

translations
  "[e | p <- xs]" => "CONST concatMap(_llc_abs p [e])xs"
  "_llc e (_llc_gen p xs) (_llc_quals q qs)"
    => "CONST concatMap(_llc_abs p (_llc e q qs))xs"
  "[e | b]" => "If b then [e] else []"
  "_llc e (_llc_guard b) (_llc_quals q qs)"
    => "If b then (_llc e q qs) else []"
  "_llc e (_llc_let b) (_llc_quals q qs)"
    => "_Let b (_llc e q qs)"

parse_translation let open HOLCF_Library in
  let
    val NilC = Syntax.const @{const_syntax "Nil"};

    fun Lam x = Syntax.const @{const_syntax "Abs_cfun"} $ x;

    fun fresh_var ts ctxt =
      let
        val ctxt' = fold Variable.declare_term ts ctxt
      in
        singleton (Variable.variant_frees ctxt' []) ("x", dummyT)
      end

    fun pat_tr ctxt p e = (* %x. case x of p => e | _ => [] *)
      let
        val x = Free (fresh_var [p, e] ctxt);
        val case1 = Syntax.const @{syntax_const "_case1"} $ p $ e;
        val case2 =
          Syntax.const @{syntax_const "_case1"} $
            Syntax.const @{const_syntax Pure.dummy_pattern} $ NilC;
        val cs = Syntax.const @{syntax_const "_case2"} $ case1 $ case2;
      in
        (* FIXME: handle HOLCF patterns correctly *)
        Syntax_Trans.abs_tr [x, Case_Translation.case_tr false ctxt [x, cs]]
        |> Lam
      end

    fun abs_tr ctxt [p, e] =
      (case Term_Position.strip_positions p of
        Free (s, T) => Lam (Syntax_Trans.abs_tr [p, e])
      | _ => pat_tr ctxt p e)

  in [(@{syntax_const "_llc_abs"}, abs_tr)] end
end

lemma concatMap_singleton [simp]:
  "concatMap(Λ x. [fx])xs = mapfxs"
  by (induct xs) simp_all

lemma listcompr_filter [simp]:
  "[x | x <- xs, Px] = filterPxs"
proof (induct xs)
  case (Cons a xs)
  then show ?case by (cases "Pa"; simp)
qed simp_all

lemma "[y | let y = x*2; z = y, x <- xs] = A"
  apply simp
  oops

end

Theory Num_Class

section ‹The Num Class›

theory Num_Class
  imports
    Type_Classes
    Data_Integer
    Data_Tuple
begin


subsection ‹Num class›

(* TODO: Show class (jb) *)

class Num_syn =
  Eq +
  plus +
  minus +
  times +
  zero +
  one +
  fixes  negate :: "'a  'a"
  and   abs :: "'a  'a"
  and   signum :: "'a  'a"
  and   fromInteger :: "Integer  'a"

(* If I create this class in one step I get problems with Num_faithful... (jb) *)
class Num = Num_syn + plus_cpo + minus_cpo + times_cpo

class Num_strict = Num +
  assumes plus_strict[simp]:
    "x +  = (::'a::Num)"
    " + x = "
  assumes minus_strict[simp]:
    "x -  = "
    " - x = "
  assumes mult_strict[simp]:
    "x *  = "
    " * x = "
  assumes negate_strict[simp]:
    "negate = "
  assumes abs_strict[simp]:
    "abs = "
  assumes signum_strict[simp]:
    "signum = "
  assumes fromInteger_strict[simp]:
    "fromInteger = "

(* TODO: How to name the type class that adds the reqiured relations? For Eq, we have Eq_equiv
resp. Eq_eq, but for Num I don't see a nice name. Also, a generic name might be nicer. (jb) *)

class Num_faithful =
  (* TODO: Why is it not possible to use Num here? I get
     Type unification failed: No type arity lift :: minus
     (jb)
  *)
  Num_syn +
  (* This is the only relation about Num required by the Haskell report. By using eq we leave it
  to the users choice of Eq_eq or Eq_equiv whether the relations should hold up to equivalence or
  syntactic equality. (jb) *)
  assumes abs_signum_eq: "(eq((absx) * (signumx))(x::'a::{Num_syn}))  TT"
  (* TODO: Should we add sensible relations on our own? (jb) *)

  (* The zero and one type class are "artificial", so we ensure they do what expected. *)
  (* Unfortunately, this is not working. Is isabelle trying to give all occurrences of 0
      the same type?
  assumes "0 = fromInteger⋅0"
  assumes "1 = fromInteger⋅1"
  *)

class Integral =
  Num +
  (* fixes quot rem  *)
  fixes div mod :: "'a  'a  ('a::Num)"
  fixes toInteger :: "'a  Integer"
begin
  (* fixrec quotRem :: "'a → 'a → ⟨'a, 'a⟩"  where "quotRem⋅x⋅y = ⟨quot⋅x⋅y, (rem⋅x⋅y)⟩" *)
  fixrec divMod :: "'a  'a  'a, 'a"  where "divModxy = divxy, modxy"

  fixrec even :: "'a  tr" where "evenx = eq(divx(fromInteger2))0"
  fixrec odd :: "'a  tr" where "oddx = neg(evenx)"
end

class Integral_strict = Integral +
  assumes div_strict[simp]:
    "divx = (::'a::Integral)"
    "divx = "
  assumes mod_strict[simp]:
    "modx = "
    "modx = "
  assumes toInteger_strict[simp]:
    "toInteger = "

class Integral_faithful =
  Integral +
  Num_faithful +
  (* assumes "¬(eq⋅y⋅0 = TT) ⟹ quot⋅x⋅y * y + rem⋅x⋅y = (x::'a::{Integral})" *)
  assumes "eqy0 = FF  divxy * y + modxy = (x::'a::{Integral})"


subsection ‹Instances for Integer›

instantiation Integer :: Num_syn
begin
  definition "negate = (Λ (MkIx). MkI(uminus x))"
  definition "abs = (Λ (MkIx) . MkI(¦x¦))"
  definition "signum = (Λ (MkIx) . MkI(sgn x))"
  definition "fromInteger = (Λ x. x)"
  instance..
end

instance Integer :: Num
  by standard

instance Integer :: Num_faithful
  apply standard
  apply (rename_tac x, case_tac x)
   apply simp
  apply (simp add: signum_Integer_def abs_Integer_def)
  apply (metis mult.commute mult_sgn_abs)
  done

instance Integer :: Num_strict
  apply standard
  unfolding signum_Integer_def abs_Integer_def negate_Integer_def fromInteger_Integer_def
  by simp_all

instantiation Integer :: Integral
begin
  definition "div = (Λ (MkIx) (MkIy). MkI(Rings.divide x y))"
  definition "mod = (Λ (MkIx) (MkIy). MkI(Rings.modulo x y))"
  definition "toInteger = (Λ x. x)"
  instance ..
end

instance Integer :: Integral_strict
  apply standard
  unfolding div_Integer_def mod_Integer_def toInteger_Integer_def
      apply simp_all
   apply (rename_tac x, case_tac x, simp, simp)+
  done

instance Integer :: Integral_faithful
  apply standard
  unfolding div_Integer_def mod_Integer_def
  apply (rename_tac y x)
  apply (case_tac y, simp)
  apply (case_tac x, simp)
  apply simp
  done

lemma Integer_Integral_simps[simp]:
  "div(MkIx)(MkIy) = MkI(Rings.divide x y)"
  "mod(MkIx)(MkIy) = MkI(Rings.modulo x y)"
  "fromIntegeri = i"
  unfolding mod_Integer_def div_Integer_def fromInteger_Integer_def by simp_all

end

Theory HOLCF_Prelude

theory HOLCF_Prelude
  imports
    HOLCF_Main
    Type_Classes
    Numeral_Cpo
    Data_Function
    Data_Bool
    Data_Tuple
    Data_Integer
    Data_List
    Data_Maybe
begin
end

Theory Fibs

theory "Fibs"
  imports
    "../HOLCF_Prelude"
    "../Definedness"
begin

section ‹Fibonacci sequence›

text ‹
  In this example, we show that the self-recursive lazy definition of the
  fibonacci sequence is actually defined and correct.
›

fixrec fibs :: "[Integer]" where
  [simp del]: "fibs = 0 : 1 : zipWith(+)fibs(tailfibs)"

fun fib :: "int  int" where
  "fib n = (if n  0 then 0 else if n = 1 then 1 else fib (n - 1) + fib (n - 2))"

declare fib.simps [simp del]

lemma fibs_0 [simp]:
  "fibs !! 0 = 0"
  by (subst fibs.simps) simp

lemma fibs_1 [simp]:
  "fibs !! 1 = 1"
  by (subst fibs.simps) simp

text ‹And the proof that @{term "fibs !! i"} is defined and the fibs value.›

(* Strange isabelle simplifier bug? *)
lemma [simp]:"-1 + i =  i  - 1" by simp
lemma [simp]:"-2 + i =  i  - 2" by simp

lemma nth_fibs:
  assumes "defined i" and " i   0" shows "defined (fibs !! i)" and " fibs !! i  = fib  i "
  using assms
proof(induction i rule:nonneg_full_Int_induct)
  case (Suc i)
  case 1
  with Suc show ?case
    apply (cases "i = 0")
     apply (subst fibs.simps, (subst fib.simps)?, simp add: nth_zipWith nth_tail)
    apply (cases "i = 1")
     apply (subst fibs.simps, (subst fib.simps)?, simp add: nth_zipWith nth_tail)
    apply (subst fibs.simps, (subst fib.simps)?, simp add: nth_zipWith nth_tail)
    done
qed (subst fibs.simps, (subst fib.simps)?, simp add: nth_zipWith nth_tail)+

end

Theory Sieve_Primes

theory Sieve_Primes
  imports
    "HOL-Computational_Algebra.Primes"
    "../Num_Class"
    "../HOLCF_Prelude"
(*FIXME: import order matters, if HOLCF_Prelude is not imported last,
constants like "filter" etc. refer to List.thy; I'm afraid however
that the current order only works by accident*)
begin

section ‹The Sieve of Eratosthenes›

declare [[coercion int]]
declare [[coercion_enabled]]

text ‹
  This example proves that the well-known Haskell two-liner that lazily
  calculates the list of all primes does indeed do so. This proof is using
  coinduction.
›

text ‹
  We need to hide some constants again since we imported something from HOL
  not via @{theory "HOLCF-Prelude.HOLCF_Main"}.
›

no_notation
  Rings.divide (infixl "div" 70) and
  Rings.modulo (infixl "mod" 70)

no_notation
  Set.member  ("(:)") and
  Set.member  ("(_/ : _)" [51, 51] 50)

text ‹This is the implementation. We also need a modulus operator.›
fixrec sieve :: "[Integer]  [Integer]" where
  "sieve(p : xs) = p : (sieve(filter(Λ x. neg(eq(modxp)0))xs))"

fixrec primes :: "[Integer]" where
  "primes = sieve[2..]"

text ‹Simplification rules for modI:›

definition MkI' :: "int  Integer" where
  "MkI' x = MkIx"

lemma MkI'_simps [simp]:
  shows "MkI' 0 = 0" and "MkI' 1 = 1" and "MkI' (numeral k) = numeral k"
  unfolding MkI'_def zero_Integer_def one_Integer_def numeral_Integer_eq
  by rule+

lemma modI_numeral_numeral [simp]:
  "mod(numeral i)(numeral j) = MkI' (Rings.modulo (numeral i) (numeral j))"
  unfolding numeral_Integer_eq mod_Integer_def MkI'_def by simp

text ‹Some lemmas demonstrating evaluation of our list:›

lemma "primes !! 0 = 2"
  unfolding primes.simps
  apply (simp only: enumFrom_intsFrom_conv)
  apply (subst intsFrom.simps)
  apply simp
  done

lemma "primes !! 1 = 3"
  unfolding primes.simps
  apply (simp only: enumFrom_intsFrom_conv)
  apply (subst intsFrom.simps)
  apply (subst intsFrom.simps)
  apply simp
  done

lemma "primes !! 2 = 5"
  unfolding primes.simps
  apply (simp only: enumFrom_intsFrom_conv)
  apply (subst intsFrom.simps)
  apply (subst intsFrom.simps)
  apply (subst intsFrom.simps)
  apply (subst intsFrom.simps)
  apply simp
  done

lemma "primes !! 3 = 7"
  unfolding primes.simps
  apply (simp only: enumFrom_intsFrom_conv)
  apply (subst intsFrom.simps)
  apply (subst intsFrom.simps)
  apply (subst intsFrom.simps)
  apply (subst intsFrom.simps)
  apply (subst intsFrom.simps)
  apply (subst intsFrom.simps)
  apply (simp del: filter_FF filter_TT)
  (* FIXME: remove these two from the default simpset *)
  done

text ‹Auxiliary lemmas about prime numbers›

lemma find_next_prime_nat:
  fixes n :: nat
  assumes "prime n"
  shows " n'. n' > n  prime n'  (k. n < k  k < n'  ¬ prime k)"
  using ex_least_nat_le[of "λ k . k > n  prime k"]
  by (metis bigger_prime not_prime_0)


text ‹Simplification for andalso:›

lemma andAlso_Def[simp]: "((Def x) andalso (Def y)) = Def (x  y)"
  by (metis Def_bool2 Def_bool4 andalso_thms(1) andalso_thms(2))

text ‹This defines the bisimulation and proves it to be a list bisimulation.›

definition prim_bisim:
  "prim_bisim x1 x2 = ( n . prime n  
    x1 = sieve(filter(Λ (MkIi). Def ((d. d > 1  d < n  ¬ (d dvd i))))[MkIn..]) 
    x2 = filter(Λ (MkIi). Def (prime (nat ¦i¦)))[MkIn..])"

lemma prim_bisim_is_bisim: "list_bisim prim_bisim"
proof -
  {
    fix xs ys
    assume "prim_bisim xs ys"
    then obtain n :: nat where
      "prime n" and
      "n > 1" and
      "xs = sieve(filter(Λ (MkIi). Def ((d::int. d > 1  d < n  ¬ (d dvd i))))[MkIn..])" (is "_ = sieve?xs") and
      "ys = filter(Λ (MkIi). Def (prime (nat ¦i¦)))[MkIn..]"
      (* sledgehammer *)
    proof -
      assume a1: "n. prime n; 1 < n; xs = sieve (Data_List.filter (Λ (MkIi). Def (d>1. d < int n  ¬ d dvd i)) [MkI(int n)..]); ys = Data_List.filter (Λ (MkIi). Def (prime (nat ¦i¦))) [MkI(int n)..]  thesis"
      obtain ii  where
        f2: "is isa. (¬ prim_bisim is isa  prime (ii is isa)  is = sieve (Data_List.filter (Λ (MkIi). Def (ia>1. ia < ii is isa  ¬ ia dvd i)) [MkI(ii is isa)..])  isa = Data_List.filter (Λ (MkIi). Def (prime (nat ¦i¦))) [MkI(ii is isa)..])  ((i. ¬ prime i  is  sieve (Data_List.filter (Λ (MkIia). Def (ib>1. ib < i  ¬ ib dvd ia)) [MkIi..])  isa  Data_List.filter (Λ (MkIi). Def (prime (nat ¦i¦))) [MkIi..])  prim_bisim is isa)"
        using prim_bisim by moura
      then have f3: "prime (ii xs ys)"
        using ‹prim_bisim xs ys by blast
      then obtain nn :: "int  nat" where
        f4: "int (nn (ii xs ys)) = ii xs ys"
        by (metis (no_types) prime_gt_0_int zero_less_imp_eq_int)
      then have "prime (nn (ii xs ys))"
        using f3 by (metis (no_types) prime_nat_int_transfer)
      then show ?thesis
        using f4 f2 a1 ‹prim_bisim xs ys prime_gt_1_nat by presburger
    qed

    obtain n' where "n' > n" and "prime n'" and not_prime: "k. n < k  k < n'  ¬ prime k"
      using find_next_prime_nat[OF ‹prime n] by auto

    {
      fix k :: int
      assume "n < k" and "k < n'"

      have  "k > 1" using n < k n > 1 by auto
      then obtain k' :: nat where "k = int k'" by (cases k) auto
      then obtain p where "prime p" and "p dvd k"
        using k > 1 k = int k'
        by (metis (full_types) less_numeral_extra(4) of_nat_1 of_nat_dvd_iff prime_factor_nat prime_nat_int_transfer)  
      then have "p < n'" using k < n'  k > 1
        using zdvd_imp_le [of p k] by simp
      then have "p  n" using ‹prime p not_prime
        using not_le prime_gt_0_int zero_less_imp_eq_int
        by (metis of_nat_less_iff prime_nat_int_transfer) 
      then have "d::int>1. d  n  d dvd k" using p dvd k ‹prime p of_nat_le_iff prime_gt_1_nat
          prime_gt_1_int by auto
    }
    then have between_have_divisors: "k::int. n < k  k < n'  d::int>1. d  n  d dvd k".

    {
      fix i
      {
        assume small: "d::int>1. d  n  ¬ d dvd i"
        fix d
        assume "1 < d" and "d dvd i" and "d < n'"
        with small have "d > n" by auto

        obtain d'::int where "d' > 1" and "d'  n" and "d' dvd d" using between_have_divisors[OF n < d d < n'] by auto
        with d dvd i small have False by (metis (full_types) dvd_trans)
      }
      then have "(d::int. d > 1  d  n  ¬ (d dvd i)) = (d::int. d > 1  d < n'  ¬ (d dvd i))"
        using n' > n by auto
    }
    then have between_not_relvant:  " i. (d::int. d > 1  d  n  ¬ (d dvd i)) = (d::int. d > 1  d < n'  ¬ (d dvd i))" .

    from ‹prime n
    have "d::int >1. d < n  ¬ d dvd n"
      unfolding prime_int_altdef using int_one_le_iff_zero_less le_less
      by (simp add: prime_int_not_dvd)
    then
    obtain xs' where "?xs = (MkIn) : xs'" and "xs' = filter(Λ (MkIi). Def ((d::int. d > 1  d < n  ¬ (d dvd i))))[MkI(n+1)..]"
      by (subst (asm) intsFrom.simps[unfolded enumFrom_intsFrom_conv[symmetric]], simp add: one_Integer_def TT_def[symmetric] add.commute)

    {
      have "filter(Λ x. neg(eq(modx(MkIn))0))(filter(Λ (MkIi). Def ((d::int. d > 1  d < n  ¬ (d dvd i))))[MkI(n+1)..])
            = filter(Λ (MkIx). Def (¬ n dvd x))(filter(Λ (MkIi). Def ((d::int. d > 1  d < n  ¬ (d dvd i))))[MkI(n+1)..])"
        apply (rule filter_cong[rule_format])
        apply (rename_tac x, case_tac x)
         apply (simp)
        apply (auto simp add: zero_Integer_def)
         apply (rule FF_def)
        apply (simp add: TT_def)
        by (metis dvd_eq_mod_eq_0)
      also have "... = filter(Λ (MkIi). Def ((¬ n dvd i)  (d::int. d > 1  d < n  ¬ (d dvd i))))[MkI(n+1)..]"
        by (auto intro!: filter_cong[rule_format])
      also have "... = filter(Λ (MkIi). Def ((d::int. d > 1  d  n  ¬ (d dvd i))))[MkI(n+1)..]"
        apply (rule filter_cong[rule_format])
        apply (rename_tac x, case_tac x)
        using n > 1
         apply auto
        done
      also have "... = filter(Λ (MkIi). Def ((d::int. d > 1  d  n  ¬ (d dvd i))))[MkI(int n+1)..]"
        by (metis (no_types, lifting) of_nat_1 of_nat_add)
      also have "... = filter(Λ (MkIi). Def ((d::int. d > 1  d  n  ¬ (d dvd i))))[MkIn'..]"
        apply (rule filter_fast_forward[of n n'])  using n' > n
         apply (auto simp add: between_have_divisors)
        done
      also have "... = filter(Λ (MkIi). Def ((d::int. d > 1  d < n'  ¬ (d dvd i))))[MkIn'..]"
        by (auto intro: filter_cong[rule_format] simp add: between_not_relvant)
      also note calculation
    }
    note tmp = this

    {
      have "xs = sieve?xs" by fact
      also have "... = sieve((MkIn) : xs')" using ?xs = _ by simp
      also have "... = sieve((MkIn) : filter(Λ (MkIi). Def ((d::int. d > 1  d < n  ¬ (d dvd i))))[MkI(n+1)..])" using xs' = _ by simp
      also have "... = (MkIn) : sieve(filter(Λ (MkIi). Def ((d::int. d > 1  d < n'  ¬ (d dvd i))))[MkIn'..])"  using tmp by simp
      also note calculation
    }
    moreover
    {
      have "ys = filter(Λ (MkIi). Def (prime (nat ¦i¦)))[MkIn..]" by fact
      also have "... = (MkIn) : filter(Λ (MkIi). Def (prime (nat ¦i¦)))[MkI(int n+1)..]"
        using ‹prime n
        by (subst intsFrom.simps[unfolded enumFrom_intsFrom_conv[symmetric]])(simp add: one_Integer_def TT_def[symmetric] add.commute)
      also have "... = (MkIn) : filter(Λ (MkIi). Def (prime (nat ¦i¦)))[MkIn'..]"
        apply (subst filter_fast_forward[of n n']) using n' > n and not_prime
          apply auto
        apply (metis (full_types) k. int n < k; k < int n'  d>1. d  int n  d dvd k le_less not_le prime_gt_0_int prime_int_not_dvd zdvd_imp_le) 
        done
      also note calculation
    }
    moreover
    note ‹prime n'
    ultimately
    have " p xs' ys'. xs = p : xs'  ys = p : ys'  prim_bisim xs' ys'" unfolding prim_bisim
      using prime_nat_int_transfer by blast
  }
  then show ?thesis unfolding list.bisim_def by metis
qed

text ‹Now we apply coinduction:›

lemma sieve_produces_primes:
  fixes n :: nat
  assumes "prime n"
  shows "sieve(filter(Λ (MkIi). Def ((d::int. d > 1  d < n  ¬ (d dvd i))))[MkIn..])
    = filter(Λ (MkIi). Def (prime (nat ¦i¦)))[MkIn..]"
  using assms
  apply -
  apply (rule list.coinduct[OF prim_bisim_is_bisim], auto simp add: prim_bisim)
  using prime_nat_int_transfer by blast

text ‹And finally show the correctness of primes.›

theorem primes:
  shows "primes = filter(Λ (MkIi). Def (prime (nat ¦i¦)))[MkI2..]"
proof -
  have "primes = sieve[2 ..]" by (rule primes.simps)
  also have "... = sieve(filter(Λ (MkIi). Def ((d::int. d > 1  d < (int 2)  ¬ (d dvd i))))[MkI(int 2)..])"
    unfolding numeral_Integer_eq
    by (subst filter_TT, auto)
  also have "... = filter(Λ (MkIi). Def (prime (nat ¦i¦)))[MkI(int 2)..]"
    by (rule sieve_produces_primes[OF two_is_prime_nat])
  also have "... = filter(Λ (MkIi). Def (prime (nat ¦i¦)))[MkI2..]"
    by simp
  finally show ?thesis .
qed

end

Theory GHC_Rewrite_Rules

section ‹GHC's "fold/build" Rule›

theory GHC_Rewrite_Rules
  imports "../HOLCF_Prelude"
begin

subsection ‹Approximating the Rewrite Rule›

text ‹
  The original rule looks as follows (see also \cite{ghc-rewriting}):
  \begin{verbatim}
  "fold/build"
    forall k z (g :: forall b. (a -> b -> b) -> b -> b).
    foldr k z (build g) = g k z
  \end{verbatim}
  Since we do not have rank-2 polymorphic types in Isabelle/HOL, we try to imitate a similar
  statement by introducing a new type that combines possible folds with their argument lists, i.e.,
  @{term f} below is a function that, in a way, represents the list @{term xs}, but where list
  constructors are functionally abstracted.
›

abbreviation (input) abstract_list where
  "abstract_list xs  (Λ c n. foldrcnxs)"

cpodef ('a, 'b) listfun =
  "{(f :: ('a  'b  'b)  'b  'b, xs). f = abstract_list xs}"
  by auto

definition listfun :: "('a, 'b) listfun  ('a  'b  'b)  'b  'b" where
  "listfun = (Λ g. Product_Type.fst (Rep_listfun g))"

definition build :: "('a, 'b) listfun  ['a]" where
  "build = (Λ g. Product_Type.snd (Rep_listfun g))"

definition augment :: "('a, 'b) listfun  ['a]  ['a]" where
  "augment = (Λ g xs. buildg ++ xs)"

definition listfun_comp :: "('a, 'b) listfun  ('a, 'b) listfun  ('a, 'b) listfun" where
  "listfun_comp = (Λ g h.
    Abs_listfun (Λ c n. listfungc(listfunhcn), buildg ++ buildh))"

abbreviation
  listfun_comp_infix :: "('a, 'b) listfun  ('a, 'b) listfun  ('a, 'b) listfun" (infixl "∘lf" 55)
  where
    "g ∘lf h  listfun_compgh"

fixrec mapFB :: "('b  'c  'c)  ('a  'b)  'a  'c  'c" where
  "mapFBcf = (Λ x ys. c(fx)ys)"


subsection ‹Lemmas›

lemma cont_listfun_body [simp]:
  "cont (λg. Product_Type.fst (Rep_listfun g))"
  by (simp add: cont_Rep_listfun)

lemma cont_build_body [simp]:
  "cont (λg. Product_Type.snd (Rep_listfun g))"
  by (simp add: cont_Rep_listfun)

lemma build_Abs_listfun:
  assumes "abstract_list xs = f"
  shows "build(Abs_listfun (f, xs)) = xs"
  using assms and Abs_listfun_inverse [of "(f, xs)"]
  by (simp add: build_def)

lemma listfun_Abs_listfun [simp]:
  assumes "abstract_list xs = f"
  shows "listfun(Abs_listfun (f, xs)) = f"
  using assms and Abs_listfun_inverse [of "(f, xs)"]
  by (simp add: listfun_def)

lemma augment_Abs_listfun [simp]:
  assumes "abstract_list xs = f"
  shows "augment(Abs_listfun (f, xs))ys = xs ++ ys"
  using assms and Abs_listfun_inverse [of "(f, xs)"]
  by (simp add: augment_def build_Abs_listfun)

lemma cont_augment_body [simp]:
  "cont (λg. Abs_cfun ((++) (Product_Type.snd (Rep_listfun g))))"
  by (simp add: cont_Rep_listfun)

lemma "fold/build":
  fixes g :: "('a, 'b) listfun"
  shows "foldrkz(buildg) = listfungkz"
proof -
  from Rep_listfun obtain f xs where
    "Rep_listfun g = (f, xs)" and "f = abstract_list xs" by blast
  then show ?thesis by (simp add: build_def listfun_def)
qed

lemma "foldr/augment":
  fixes g :: "('a, 'b) listfun"
  shows "foldrkz(augmentgxs) = listfungk(foldrkzxs)"
proof -
  from Rep_listfun obtain f ys where
    "Rep_listfun g = (f, ys)" and "f = abstract_list ys" by blast
  then show ?thesis
    by (simp add: augment_def build_def listfun_def)
qed

lemma "foldr/id":
  "foldr(:)[] = (Λ x. x)"
proof (rule cfun_eqI)
  fix xs :: "['a]"
  show "foldr(:)[]xs = (Λ x. x)xs"
    by (induction xs) simp+
qed

lemma "foldr/app":
  "foldr(:)ys = (Λ xs. xs ++ ys)"
proof (rule cfun_eqI)
  fix xs :: "['a]"
  show "foldr(:)ysxs = (Λ xs. xs ++ ys)xs" by (induct xs) simp+
qed

lemma "foldr/cons": "foldrkz(x:xs) = kx(foldrkzxs)" by simp
lemma "foldr/single": "foldrkz[x] = kxz" by simp
lemma "foldr/nil": "foldrkz[] = z" by simp

lemma cont_listfun_comp_body1 [simp]:
  "cont (λh. Abs_listfun (Λ c n. listfungc(listfunhcn), buildg ++ buildh))"
proof -
  have "h.
    (Λ c n. listfungc(listfunhcn), buildg ++ buildh)  {(f, xs). f = abstract_list xs}"
    by (simp add: "fold/build")
  from cont_Abs_listfun [OF this, of "λx. x"]
  show ?thesis by simp
qed

lemma cont_listfun_comp_body2 [simp]:
  "cont (λg. Abs_listfun (Λ c n. listfungc(listfunhcn), buildg ++ buildh))"
proof -
  have "g.
    (Λ c n. listfungc(listfunhcn), buildg ++ buildh)  {(f, xs). f = abstract_list xs}"
    by (simp add: "fold/build")
  from cont_Abs_listfun [OF this, of "λx. x"]
  show ?thesis by simp
qed

lemma cont_listfun_comp_body [simp]:
  "cont (λg. Λ h. Abs_listfun (Λ c n. listfungc(listfunhcn), buildg ++ buildh))"
  by (rule cont2cont_LAM) simp+

lemma abstract_list_build_append:
  "abstract_list (buildg ++ buildh) = (Λ c n. listfungc(listfunhcn))"
  by (intro cfun_eqI) (simp add: "fold/build")

lemma "augment/build":
  "augmentg(buildh) = build(g ∘lf h)"
  by (simp add: listfun_comp_def augment_def build_Abs_listfun [OF abstract_list_build_append])

lemma "augment/nil":
  "augmentg[] = buildg"
  by (simp add: augment_def)

lemma build_listfun_comp [simp]:
  "build(g ∘lf h) = buildg ++ buildh"
  unfolding "augment/build" [symmetric]
  by (simp add: augment_def)

lemma augment_augment:
  "augmentg(augmenthxs) = augment(g ∘lf h)xs"
  by (simp add: augment_def)

lemma abstract_list_map [simp]:
  "abstract_list (mapfxs) = (Λ c n. foldr(mapFBcf)nxs)"
  by (intro cfun_eqI, induct xs) simp+

lemma "map":
  "mapfxs = build(Abs_listfun (Λ c n. foldr(mapFBcf)nxs, mapfxs))"
  by (simp add: build_Abs_listfun)

lemma "mapList":
  "foldr(mapFB(:)