Theory PDF_Target_Semantics

(*
  Theory: PDF_Target_Semantics.thy
  Authors: Manuel Eberl

  The semantics of the target language.
*)

section ‹Target Language Syntax and Semantics›

theory PDF_Target_Semantics
imports PDF_Semantics
begin

datatype cexpr =
    CVar vname
  | CVal val
  | CPair cexpr cexpr ("<_, _>c" [0, 0] 1000)
  | COperator pdf_operator cexpr (infixl "$$c" 999)
  | CIf cexpr cexpr cexpr ("IFc _ THEN _ ELSE _" [0, 0, 10] 10)
  | CIntegral cexpr pdf_type ("c _ _" [61] 110)

abbreviation (input) cexpr_fun :: "(cexpr  cexpr)  cexpr" (binder "λc" 10) where
  "cexpr_fun f  f (CVar 0)"
abbreviation cexpr_Add (infixl "+c" 65) where
  "cexpr_Add a b  Add $$c <a, b>c"
abbreviation cexpr_Minus ("-c _" [81] 80) where
  "cexpr_Minus a  Minus $$c a"
abbreviation cexpr_Sub (infixl "-c" 65) where
  "cexpr_Sub a b  a +c -cb"
abbreviation cexpr_Mult (infixl "*c" 70) where
  "cexpr_Mult a b  Mult $$c <a, b>c"
abbreviation "inversec e Inverse $$c e"
abbreviation cexpr_Div (infixl "'/c" 70) where
  "cexpr_Div a b  a *c inversec b"
abbreviation "factc e  Fact $$c e"
abbreviation "sqrtc e  Sqrt $$c e"
abbreviation "expc e  Exp $$c e"
abbreviation "lnc e  Ln $$c e"
abbreviation "fstc e  Fst $$c e"
abbreviation "sndc e  Snd $$c e"
abbreviation cexpr_Pow (infixl "^c" 75) where
  "cexpr_Pow a b  Pow $$c <a, b>c"
abbreviation cexpr_And (infixl "c" 35) where
  "cexpr_And a b  And $$c <a, b>c"
abbreviation cexpr_Or (infixl "c" 30) where
  "cexpr_Or a b  Or $$c <a, b>c"
abbreviation cexpr_Not ("¬c _" [40] 40) where
  "cexpr_Not a  Not $$c a"
abbreviation cexpr_Equals (infixl "=c" 70) where
  "cexpr_Equals a b  Equals $$c <a, b>c"
abbreviation cexpr_Less (infixl "<c" 70) where
  "cexpr_Less a b  Less $$c <a, b>c"
abbreviation cexpr_LessEq (infixl "c" 70) where
  "cexpr_LessEq a b  a =c b c a <c b"
abbreviation cexpr_RealCast ("_c" [0] 90) where
  "cexpr_RealCast a  Cast REAL $$c a"
abbreviation CReal where
  "CReal x  CVal (RealVal x)"
abbreviation CInt where
  "CInt x  CVal (IntVal x)"
abbreviation πc where
  "πc  Pi $$c (CVal UnitVal)"

instantiation cexpr :: expr
begin

primrec free_vars_cexpr :: "cexpr  vname set" where
  "free_vars_cexpr (CVar x) = {x}"
| "free_vars_cexpr (CVal _) = {}"
| "free_vars_cexpr (oper $$c e) = free_vars_cexpr e"
| "free_vars_cexpr (<e1, e2>c) = free_vars_cexpr e1  free_vars_cexpr e2"
| "free_vars_cexpr (IFc b THEN e1 ELSE e2) =
       free_vars_cexpr b  free_vars_cexpr e1  free_vars_cexpr e2"
| "free_vars_cexpr (c e t) = Suc -` free_vars_cexpr e"

instance ..
end

inductive cexpr_typing :: "tyenv  cexpr  pdf_type  bool" ("(1_/ c/ (_ :/ _))" [50,0,50] 50) where
  cet_val:    "Γ c CVal v: val_type v"
| cet_var:    "Γ c CVar x : Γ x"
| cet_pair:   "Γ c e1 : t1  Γ c e2 : t2  Γ c <e1, e2>c : PRODUCT t1 t2"
| cet_op:     "Γ c e : t  op_type oper t = Some t'  Γ c oper $$c e : t'"
| cet_if:     "Γ c b : BOOL  Γ c e1 : t  Γ c e2 : t
                    Γ c IFc b THEN e1 ELSE e2 : t"
| cet_int:    "t  Γ c e : REAL  Γ c c e t : REAL"

lemma cet_val': "t = val_type v  Γ c CVal v : t"
  by (simp add: cet_val)

lemma cet_var': "t = Γ x  Γ c CVar x : t"
  by (simp add: cet_var)

lemma cet_not: "Γ c e : BOOL  Γ c ¬c e : BOOL"
  by (intro cet_op[where t = "BOOL"] cet_pair, simp, simp)

lemma cet_and: "Γ c e1 : BOOL  Γ c e2 : BOOL  Γ c e1 c e2 : BOOL" and
      cet_or: "Γ c e1 : BOOL  Γ c e2 : BOOL  Γ c e1 c e2 : BOOL"
  by (intro cet_op[where t = "PRODUCT BOOL BOOL"] cet_pair, simp, simp, simp)+

lemma cet_minus_real: "Γ c e : REAL  Γ c -ce : REAL" and
      cet_inverse: "Γ c e : REAL  Γ c inversec e : REAL" and
      cet_sqrt: "Γ c e : REAL  Γ c sqrtc e : REAL" and
      cet_exp: "Γ c e : REAL  Γ c expc e : REAL" and
      cet_ln: "Γ c e : REAL  Γ c lnc e : REAL"
  by (rule cet_op[where t = "REAL"], simp, simp)+

lemma cet_pow_real: "Γ c e1 : REAL  Γ c e2 : INTEG  Γ c e1 ^c e2 : REAL"
  by (intro cet_op[where t = "PRODUCT REAL INTEG"] cet_pair) simp_all

lemma cet_add_real: "Γ c e1 : REAL  Γ c e2 : REAL  Γ c e1 +c e2 : REAL" and
      cet_mult_real: "Γ c e1 : REAL  Γ c e2 : REAL  Γ c e1 *c e2 : REAL" and
      cet_less_real: "Γ c e1 : REAL  Γ c e2 : REAL  Γ c e1 <c e2 : BOOL"
  by (intro cet_op[where t = "PRODUCT REAL REAL"] cet_pair, simp, simp, simp)+


lemma cet_eq: "Γ c e1 : t  Γ c e2 : t  Γ c e1 =c e2 : BOOL"
  by (intro cet_op[where t = "PRODUCT t t"] cet_pair, simp, simp, simp)+

lemma cet_less_eq_real: "Γ c e1 : REAL  Γ c e2 : REAL  Γ c e1 c e2 : BOOL"
  by (intro cet_less_real cet_or cet_eq)

lemma cet_minus_int: "Γ c e : INTEG  Γ c -ce : INTEG"
  by (rule cet_op[where t = "INTEG"], simp, simp)+

lemma cet_add_int: "Γ c e1 : INTEG  Γ c e2 : INTEG  Γ c e1 +c e2 : INTEG" and
      cet_mult_int: "Γ c e1 : INTEG  Γ c e2 : INTEG  Γ c e1 *c e2 : INTEG" and
      cet_less_int: "Γ c e1 : INTEG  Γ c e2 : INTEG  Γ c e1 <c e2 : BOOL"
  by (intro cet_op[where t = "PRODUCT INTEG INTEG"] cet_pair, simp, simp, simp)+

lemma cet_less_eq_int: "Γ c e1 : INTEG  Γ c e2 : INTEG  Γ c e1 c e2 : BOOL"
  by (intro cet_less_int cet_or cet_eq)

lemma cet_sub_int: "Γ c e1 : INTEG  Γ c e2 : INTEG  Γ c e1 -c e2 : INTEG"
  by (intro cet_minus_int cet_add_int)

lemma cet_fst: "Γ c e : PRODUCT t t'  Γ c fstc e : t" and
      cet_snd: "Γ c e : PRODUCT t t'  Γ c sndc e : t'"
  by (erule cet_op, simp)+

lemma cet_cast_real: "Γ c e : BOOL  Γ c ec : REAL"
  by (intro cet_op[where t = BOOL]) simp_all

lemma cet_cast_real_int: "Γ c e : INTEG  Γ c ec : REAL"
  by (intro cet_op[where t = INTEG]) simp_all

lemma cet_sub_real: "Γ c e1 : REAL  Γ c e2 : REAL  Γ c e1 -c e2 : REAL"
  by (intro cet_minus_real cet_add_real)

lemma cet_pi: "Γ c πc : REAL"
  by (rule cet_op, rule cet_val, simp)

lemmas cet_op_intros =
  cet_minus_real cet_exp cet_sqrt cet_ln cet_inverse cet_pow_real cet_pi
  cet_cast_real cet_add_real cet_mult_real cet_less_real
  cet_not cet_and cet_or

inductive_cases cexpr_typing_valE[elim]:  "Γ c CVal v : t"
inductive_cases cexpr_typing_varE[elim]:  "Γ c CVar x : t"
inductive_cases cexpr_typing_pairE[elim]: "Γ c <e1, e2>c : t"
inductive_cases cexpr_typing_opE[elim]:   "Γ c oper $$c e : t"
inductive_cases cexpr_typing_ifE[elim]:   "Γ c IFc b THEN e1 ELSE e2 : t"
inductive_cases cexpr_typing_intE[elim]:  "Γ c c e t : t'"

primrec cexpr_type :: "tyenv  cexpr  pdf_type option" where
  "cexpr_type _ (CVal v) = Some (val_type v)"
| "cexpr_type Γ (CVar x) = Some (Γ x)"
| "cexpr_type Γ (<e1, e2>c) = (case (cexpr_type Γ e1, cexpr_type Γ e2) of
                                (Some t1, Some t2)  Some (PRODUCT t1 t2)
                              | _  None)"
| "cexpr_type Γ (oper $$c e) = (case cexpr_type Γ e of
                                 Some t  op_type oper t
                               | _  None)"
| "cexpr_type Γ (IFc b THEN e1 ELSE e2) =
                              (if cexpr_type Γ b = Some BOOL then
                                 case (cexpr_type Γ e1, cexpr_type Γ e2) of
                                   (Some t, Some t')  if t = t' then Some t else None
                                 | _  None
                               else None)"
| "cexpr_type Γ (c e t) =
      (if cexpr_type (case_nat t Γ) e = Some REAL then Some REAL else None)"

lemma cexpr_type_Some_iff: "cexpr_type Γ e = Some t  Γ c e : t"
  apply rule
  apply (induction e arbitrary: Γ t,
         auto intro!: cexpr_typing.intros split: option.split_asm if_split_asm) []
  apply (induction rule: cexpr_typing.induct, auto)
  done

lemmas cexpr_typing_code[code_unfold] = cexpr_type_Some_iff[symmetric]

lemma cexpr_typing_cong':
  assumes "Γ c e : t" "x. x  free_vars e  Γ x = Γ' x"
  shows "Γ' c e : t"
using assms
proof (induction arbitrary: Γ' rule: cexpr_typing.induct)
  case (cet_int t Γ e Γ')
  hence "x. x  free_vars e  case_nat t Γ x = case_nat t Γ' x"
    by (auto split: nat.split)
  from cet_int.IH[OF this] show ?case by (auto intro!: cexpr_typing.intros)
qed (auto intro!: cexpr_typing.intros)

lemma cexpr_typing_cong:
  assumes "x. x  free_vars e  Γ x = Γ' x"
  shows "Γ c e : t  Γ' c e : t"
  by (rule iffI) (erule cexpr_typing_cong', simp add: assms)+


primrec cexpr_sem :: "state  cexpr  val" where
  "cexpr_sem σ (CVal v) = v"
| "cexpr_sem σ (CVar x) = σ x"
| "cexpr_sem σ <e1, e2>c = <|cexpr_sem σ e1, cexpr_sem σ e2|>"
| "cexpr_sem σ (oper $$c e) = op_sem oper (cexpr_sem σ e)"
| "cexpr_sem σ (IFc b THEN e1 ELSE e2) = (if cexpr_sem σ b = TRUE then cexpr_sem σ e1 else cexpr_sem σ e2)"
| "cexpr_sem σ (c e t) = RealVal (x. extract_real (cexpr_sem (x  σ) e) (stock_measure t))"

definition cexpr_equiv :: "cexpr  cexpr  bool" where
  "cexpr_equiv e1 e2  σ. cexpr_sem σ e1 = cexpr_sem σ e2"

lemma cexpr_equiv_commute: "cexpr_equiv e1 e2  cexpr_equiv e2 e1"
  by (auto simp: cexpr_equiv_def)


lemma val_type_cexpr_sem[simp]:
  assumes "Γ c e : t" "free_vars e  V" "σ  space (state_measure V Γ)"
  shows "val_type (cexpr_sem σ e) = t"
using assms by (induction arbitrary: σ V rule: cexpr_typing.induct)
               (auto intro: state_measure_var_type op_sem_val_type)

lemma cexpr_sem_eq_on_vars:
  assumes "x. x  free_vars e  σ x = σ' x"
  shows "cexpr_sem σ e = cexpr_sem σ' e"
using assms
proof (induction e arbitrary: σ σ')
  case (CPair e1 e2 σ σ')
  from CPair.prems show ?case by (auto intro!: CPair.IH)
next
  case (COperator oper e σ σ')
  from COperator.prems show ?case by (auto simp: COperator.IH[of σ σ'])
next
  case (CIf b e1 e2 σ σ')
  from CIf.prems show ?case by (auto simp: CIf.IH[of σ σ'])
next
  case (CIntegral e t σ σ')
  have "cexpr_sem σ (c e t) = RealVal (x. extract_real (cexpr_sem (case_nat x σ) e) stock_measure t)"
    by simp
  also from CIntegral.prems have A: "(λv. cexpr_sem (case_nat v σ) e) = (λv. cexpr_sem (case_nat v σ') e)"
    by (intro ext CIntegral.IH) (auto split: nat.split)
  also have "RealVal (x. extract_real (cexpr_sem (case_nat x σ') e) stock_measure t) = cexpr_sem σ' (c e t)"
    by simp
  finally show ?case .
qed simp_all


definition eval_cexpr :: "cexpr  state  val  real" where
  "eval_cexpr e σ v = extract_real (cexpr_sem (case_nat v σ) e)"

lemma measurable_cexpr_sem[measurable]:
  "Γ c e : t  free_vars e  V 
      (λσ. cexpr_sem σ e)  measurable (state_measure V Γ) (stock_measure t)"
proof (induction arbitrary: V rule: cexpr_typing.induct)
  case (cet_op oper t t' Γ e)
      thus ?case using measurable_op_sem by simp
next
  case (cet_int t Γ e)
  interpret sigma_finite_measure "stock_measure t" by simp
  let ?M = "(ΠM xV. stock_measure (Γ x)) M stock_measure t"
  let ?N = "embed_measure lborel RealVal"
  have *[measurable]: "(λa. cexpr_sem a e)  measurable (state_measure (shift_var_set V) (case_nat t Γ)) REAL"
    using cet_int.prems subset_shift_var_set
    by (intro cet_int.IH) simp
  show ?case
    by simp
qed (simp_all add: state_measure_def inj_PairVal)

lemma measurable_eval_cexpr[measurable]:
  assumes "case_nat t Γ c e : REAL"
  assumes "free_vars e  shift_var_set V"
  shows "case_prod (eval_cexpr e)  borel_measurable (state_measure V Γ M stock_measure t)"
  unfolding eval_cexpr_def[abs_def] using measurable_cexpr_sem[OF assms] by simp

lemma cexpr_sem_Add:
  assumes "Γ c e1 : REAL" "Γ c e2 : REAL"
  assumes "σ  space (state_measure V Γ)" "free_vars e1  V" "free_vars e2  V"
  shows "extract_real (cexpr_sem σ (e1 +c e2)) = extract_real (cexpr_sem σ e1) + extract_real (cexpr_sem σ e2)"
  using val_type_cexpr_sem[OF assms(1,4,3)] val_type_cexpr_sem[OF assms(2,5,3)]
  by (auto simp: lift_RealIntVal2_def extract_real_def split: val.split)

lemma cexpr_sem_Mult:
  assumes "Γ c e1 : REAL" "Γ c e2 : REAL"
  assumes "σ  space (state_measure V Γ)" "free_vars e1  V" "free_vars e2  V"
  shows "extract_real (cexpr_sem σ (e1 *c e2)) = extract_real (cexpr_sem σ e1) * extract_real (cexpr_sem σ e2)"
  using val_type_cexpr_sem[OF assms(1,4,3)] val_type_cexpr_sem[OF assms(2,5,3)]
  by (auto simp: lift_RealIntVal2_def extract_real_def split: val.split)


subsection ‹General functions on Expressions›

text ‹
   Transform variable names in an expression.
›
primrec map_vars :: "(vname  vname)  cexpr  cexpr" where
  "map_vars f (CVal v) = CVal v"
| "map_vars f (CVar x) = CVar (f x)"
| "map_vars f (<e1, e2>c) = <map_vars f e1, map_vars f e2>c"
| "map_vars f (oper $$c e) = oper $$c (map_vars f e)"
| "map_vars f (IFc b THEN e1 ELSE e2) = (IFc map_vars f b THEN map_vars f e1 ELSE map_vars f e2)"
| "map_vars f (c e t) = c map_vars (case_nat 0 (λx. Suc (f x))) e t"

lemma free_vars_map_vars[simp]:
  "free_vars (map_vars f e) = f ` free_vars e"
proof (induction e arbitrary: f)
  case (CIntegral e t f)
  {
    fix x A assume "Suc x  A"
    hence "Suc (f x)  case_nat 0 (λx. Suc (f x)) ` A"
      by (subst image_iff, intro bexI[of _ "Suc x"]) (simp split: nat.split)
  }
  with CIntegral show ?case by (auto split: nat.split_asm)
qed auto

lemma cexpr_typing_map_vars:
  "Γ  f c e : t  Γ c map_vars f e : t"
proof (induction "Γ  f" e t arbitrary: Γ f rule: cexpr_typing.induct)
  case (cet_int t e Γ)
  have "case_nat t (Γ  f) = case_nat t Γ  (case_nat 0 (λx. Suc (f x)))"
    by (intro ext) (auto split: nat.split)
  from cet_int(2)[OF this] show ?case by (auto intro!: cexpr_typing.intros)
qed (auto intro!: cexpr_typing.intros)

lemma cexpr_sem_map_vars:
  "cexpr_sem σ (map_vars f e) = cexpr_sem (σ  f) e"
proof (induction e arbitrary: σ f)
  case (CIntegral e t σ f)
  {
    fix x
    have "cexpr_sem (case_nat x σ) (map_vars (case_nat 0 (λx. Suc (f x))) e) =
                 cexpr_sem (case_nat x σ  case_nat 0 (λx. Suc (f x))) e"
      by (rule CIntegral.IH)
    also have "case_nat x σ  case_nat 0 (λx. Suc (f x)) = case_nat x (λa. σ (f a))"
      by (intro ext) (auto simp add: o_def split: nat.split)
    finally have "cexpr_sem (case_nat x σ) (map_vars (case_nat 0 (λx. Suc (f x))) e) =
                      cexpr_sem (case_nat x (λa. σ (f a))) e" .
  }
  thus ?case by simp
qed simp_all

definition insert_var :: "vname  (vname  'a)  'a  vname  'a" where
  "insert_var v f x w  if w = v then x else if w > v then f (w - 1) else f w"

lemma insert_var_0[simp]: "insert_var 0 f x = case_nat x f"
  by (intro ext) (simp add: insert_var_def split: nat.split)

text ‹
  Substitutes expression e for variable x in e'.
›
primrec cexpr_subst :: "vname  cexpr  cexpr  cexpr" where
  "cexpr_subst _ _ (CVal v) = CVal v"
| "cexpr_subst x e (CVar y) = insert_var x CVar e y"
| "cexpr_subst x e <e1, e2>c = <cexpr_subst x e e1, cexpr_subst x e e2>c"
| "cexpr_subst x e (oper $$c e') = oper $$c (cexpr_subst x e e')"
| "cexpr_subst x e (IFc b THEN e1 ELSE e2) =
      (IFc cexpr_subst x e b THEN cexpr_subst x e e1 ELSE cexpr_subst x e e2)"
| "cexpr_subst x e (c e' t) = (c cexpr_subst (Suc x) (map_vars Suc e) e' t)"

lemma cexpr_sem_cexpr_subst_aux:
    "cexpr_sem σ (cexpr_subst x e e') = cexpr_sem (insert_var x σ (cexpr_sem σ e)) e'"
proof (induction e' arbitrary: x e σ)
  case (CIntegral e' t x e σ)
    have A: "y. insert_var (Suc x) (case_nat y σ) (cexpr_sem σ e) =
                    case_nat y (insert_var x σ (cexpr_sem σ e))"
      by (intro ext) (simp add: insert_var_def split: nat.split)
  show ?case by (simp add: o_def A cexpr_sem_map_vars CIntegral.IH)
qed (simp_all add: insert_var_def)

text ‹
   This corresponds to a Let-binding; the variable with index 0 is substituted
   with the given expression.
›
lemma cexpr_sem_cexpr_subst:
    "cexpr_sem σ (cexpr_subst 0 e e') = cexpr_sem (case_nat (cexpr_sem σ e) σ) e'"
  using cexpr_sem_cexpr_subst_aux by simp

lemma cexpr_typing_subst_aux:
  assumes "insert_var x Γ t c e' : t'" "Γ c e : t"
  shows "Γ c cexpr_subst x e e' : t'"
using assms
proof (induction e' arbitrary: x Γ e t')
  case CVar
  thus ?case by (auto intro!: cexpr_typing.intros simp: insert_var_def)
next
  case COperator
  thus ?case by (auto simp: cexpr_type_Some_iff[symmetric] split: option.split_asm)
next
  case (CIntegral e' t'')
  have t': "t' = REAL" using CIntegral.prems(1) by auto
  have "case_nat t'' (insert_var x Γ t) c e' : t'" using CIntegral.prems(1) by auto
  also have "case_nat t'' (insert_var x Γ t) = insert_var (Suc x) (case_nat t'' Γ) t"
    by (intro ext) (simp add: insert_var_def split: nat.split)
  finally have "insert_var (Suc x) (case_nat t'' Γ) t c e' : t'" .
  moreover from CIntegral.prems(2) have "case_nat t'' Γ c map_vars Suc e : t"
    by (intro cexpr_typing_map_vars) (simp add: o_def)
  ultimately have "case_nat t'' Γ c cexpr_subst (Suc x) (map_vars Suc e) e' : t'"
    by (rule CIntegral.IH)
  thus ?case by (auto intro: cet_int simp: t')
qed (auto intro!: cexpr_typing.intros)

lemma cexpr_typing_subst[intro]:
  assumes "Γ c e : t" "case_nat t Γ c e' : t'"
  shows "Γ c cexpr_subst 0 e e' : t'"
  using cexpr_typing_subst_aux assms by simp


lemma free_vars_cexpr_subst_aux:
  "free_vars (cexpr_subst x e e')  (λy. if y  x then y + 1 else y) -` free_vars e'  free_vars e"
    (is "free_vars _  ?f x -` _  _")
proof (induction e' arbitrary: x e)
  case (CVar y x e)
  show ?case by (auto simp: insert_var_def)
next
  case (CPair e'1 e'2 x e)
  from CPair.IH[of x e] show ?case by auto
next
  case (COperator _ _ x e)
  from COperator.IH[of x e] show ?case by auto
next
  case (CIf b e'1 e'2 x e)
  from CIf.IH[of x e] show ?case by auto
next
  case (CIntegral e' t x e)
  have "free_vars (cexpr_subst x e (c e' t)) 
          Suc -` (?f (Suc x) -` free_vars e') 
          Suc -` (free_vars (map_vars Suc e))" (is "_  ?A  ?B")
    by (simp only: cexpr_subst.simps free_vars_cexpr.simps
                   vimage_mono CIntegral.IH vimage_Un[symmetric])
  also have "?B = free_vars e" by (simp add: inj_vimage_image_eq)
  also have "?A  ?f x -` free_vars (c e' t)" by auto
  finally show ?case by blast
qed simp_all

lemma free_vars_cexpr_subst:
    "free_vars (cexpr_subst 0 e e')  Suc -` free_vars e'  free_vars e"
  by (rule order.trans[OF free_vars_cexpr_subst_aux]) (auto simp: shift_var_set_def)



primrec cexpr_comp_aux :: "vname  cexpr  cexpr  cexpr" where
  "cexpr_comp_aux _ _ (CVal v) = CVal v"
| "cexpr_comp_aux x e (CVar y) = (if x = y then e else CVar y)"
| "cexpr_comp_aux x e <e1, e2>c = <cexpr_comp_aux x e e1, cexpr_comp_aux x e e2>c"
| "cexpr_comp_aux x e (oper $$c e') = oper $$c (cexpr_comp_aux x e e')"
| "cexpr_comp_aux x e (IFc b THEN e1 ELSE e2) =
      (IFc cexpr_comp_aux x e b THEN cexpr_comp_aux x e e1 ELSE cexpr_comp_aux x e e2)"
| "cexpr_comp_aux x e (c e' t) = (c cexpr_comp_aux (Suc x) (map_vars Suc e) e' t)"

lemma cexpr_sem_cexpr_comp_aux:
    "cexpr_sem σ (cexpr_comp_aux x e e') = cexpr_sem (σ(x := cexpr_sem σ e)) e'"
proof (induction e' arbitrary: x e σ)
  case (CIntegral e' t x e σ)
  have "y. (case_nat y σ)(Suc x := cexpr_sem (case_nat y σ) (map_vars Suc e)) =
                 case_nat y (σ(x := cexpr_sem σ e))"
    by (intro ext) (auto simp: cexpr_sem_map_vars o_def split: nat.split)
  thus ?case by (auto intro!: integral_cong simp: CIntegral.IH simp del: fun_upd_apply)
qed (simp_all add: insert_var_def)

definition cexpr_comp (infixl "c" 55) where
  "cexpr_comp b a  cexpr_comp_aux 0 a b"

lemma cexpr_typing_cexpr_comp_aux:
  assumes "Γ(x := t1) c e' : t2" "Γ c e : t1"
  shows "Γ c cexpr_comp_aux x e e' : t2"
using assms
proof (induction e' arbitrary: Γ e x t2)
  case COperator
  thus ?case by (elim cexpr_typing_opE) (auto intro!: cexpr_typing.intros) []
next
  case CPair
  thus ?case by (elim cexpr_typing_pairE) (auto intro!: cexpr_typing.intros) []
next
  case (CIntegral e' t Γ e x t2)
  from CIntegral.prems have [simp]: "t2 = REAL" by auto
  from CIntegral.prems have "case_nat t (Γ(x := t1)) c e' : REAL" by (elim cexpr_typing_intE)
  also have "case_nat t (Γ(x := t1)) = (case_nat t Γ)(Suc x := t1)"
    by (intro ext) (simp split: nat.split)
  finally have "... c e' : REAL" .
  thus "Γ c cexpr_comp_aux x e (c e' t) : t2"
    by (auto intro!: cexpr_typing.intros CIntegral.IH cexpr_typing_map_vars
             simp: o_def CIntegral.prems)
qed (auto intro!: cexpr_typing.intros)

lemma cexpr_typing_cexpr_comp[intro]:
  assumes "case_nat t1 Γ c g : t2"
  assumes "case_nat t2 Γ c f : t3"
  shows "case_nat t1 Γ c f c g : t3"
proof (unfold cexpr_comp_def, intro cexpr_typing_cexpr_comp_aux)
  have "(case_nat t1 Γ)(0 := t2) = case_nat t2 Γ"
    by (intro ext) (simp split: nat.split)
  with assms show "(case_nat t1 Γ)(0 := t2) c f : t3" by simp
qed (insert assms)


lemma free_vars_cexpr_comp_aux:
  "free_vars (cexpr_comp_aux x e e')  (free_vars e' - {x})  free_vars e"
proof (induction e' arbitrary: x e)
  case (CIntegral e' t x e)
  note IH = CIntegral.IH[of "Suc x" "map_vars Suc e"]
  have "free_vars (cexpr_comp_aux x e (c e' t)) =
           Suc -` free_vars (cexpr_comp_aux (Suc x) (map_vars Suc e) e')" by simp
  also have "...  Suc -` (free_vars e' - {Suc x}  free_vars (map_vars Suc e))"
    by (rule vimage_mono, rule CIntegral.IH)
  also have "...  free_vars (c e' t) - {x}  free_vars e"
    by (auto simp add: vimage_Diff vimage_image_eq)
  finally show ?case .
qed (simp, blast?)+

lemma free_vars_cexpr_comp:
  "free_vars (cexpr_comp e e')  (free_vars e - {0})  free_vars e'"
  by (simp add: free_vars_cexpr_comp_aux cexpr_comp_def)

lemma free_vars_cexpr_comp':
  "free_vars (cexpr_comp e e')  free_vars e  free_vars e'"
  using free_vars_cexpr_comp by blast

lemma cexpr_sem_cexpr_comp:
    "cexpr_sem σ (f c g) = cexpr_sem (σ(0 := cexpr_sem σ g)) f"
  unfolding cexpr_comp_def by (simp add: cexpr_sem_cexpr_comp_aux)

lemma eval_cexpr_comp:
    "eval_cexpr (f c g) σ x = eval_cexpr f σ (cexpr_sem (case_nat x σ) g)"
proof-
  have "(case_nat x σ)(0 := cexpr_sem (case_nat x σ) g) = case_nat (cexpr_sem (case_nat x σ) g) σ"
    by (intro ext) (auto split: nat.split)
  thus ?thesis by (simp add: eval_cexpr_def cexpr_sem_cexpr_comp)
qed

primrec cexpr_subst_val_aux :: "nat  cexpr  val  cexpr" where
  "cexpr_subst_val_aux _ (CVal v) _ = CVal v"
| "cexpr_subst_val_aux x (CVar y) v = insert_var x CVar (CVal v) y"
| "cexpr_subst_val_aux x (IFc b THEN e1 ELSE e2) v =
    (IFc cexpr_subst_val_aux x b v THEN cexpr_subst_val_aux x e1 v ELSE cexpr_subst_val_aux x e2 v)"
| "cexpr_subst_val_aux x (oper $$c e) v = oper $$c (cexpr_subst_val_aux x e v)"
| "cexpr_subst_val_aux x <e1, e2>c v = <cexpr_subst_val_aux x e1 v, cexpr_subst_val_aux x e2 v>c"
| "cexpr_subst_val_aux x (c e t) v = c cexpr_subst_val_aux (Suc x) e v t"

lemma cexpr_subst_val_aux_eq_cexpr_subst:
    "cexpr_subst_val_aux x e v = cexpr_subst x (CVal v) e"
  by (induction e arbitrary: x) simp_all

definition cexpr_subst_val :: "cexpr  val  cexpr" where
  "cexpr_subst_val e v  cexpr_subst_val_aux 0 e v"

lemma cexpr_sem_cexpr_subst_val[simp]:
    "cexpr_sem σ (cexpr_subst_val e v) = cexpr_sem (case_nat v σ) e"
  by (simp add: cexpr_subst_val_def cexpr_subst_val_aux_eq_cexpr_subst cexpr_sem_cexpr_subst)

lemma cexpr_typing_subst_val[intro]:
    "case_nat t Γ c e : t'  val_type v = t  Γ c cexpr_subst_val e v : t'"
  by (auto simp: cexpr_subst_val_def cexpr_subst_val_aux_eq_cexpr_subst intro!: cet_val')

lemma free_vars_cexpr_subst_val_aux:
    "free_vars (cexpr_subst_val_aux x e v) = (λy. if y  x then Suc y else y) -` free_vars e"
  by (induction e arbitrary: x) (auto simp: insert_var_def split: if_split_asm)

lemma free_vars_cexpr_subst_val[simp]:
    "free_vars (cexpr_subst_val e v) = Suc -` free_vars e"
  by (simp add: cexpr_subst_val_def free_vars_cexpr_subst_val_aux)


subsection ‹Nonnegative expressions›

definition "nonneg_cexpr V Γ e 
    σ  space (state_measure V Γ). extract_real (cexpr_sem σ e)  0"

lemma nonneg_cexprI:
    "(σ. σ  space (state_measure V Γ)  extract_real (cexpr_sem σ e)  0)  nonneg_cexpr V Γ e"
  unfolding nonneg_cexpr_def by simp

lemma nonneg_cexprD:
    "nonneg_cexpr V Γ e  σ  space (state_measure V Γ)  extract_real (cexpr_sem σ e)  0"
  unfolding nonneg_cexpr_def by simp

lemma nonneg_cexpr_map_vars:
  assumes "nonneg_cexpr (f -` V) (Γ  f) e"
  shows "nonneg_cexpr V Γ (map_vars f e)"
  by (intro nonneg_cexprI, subst cexpr_sem_map_vars, intro nonneg_cexprD[OF assms])
     (auto simp: state_measure_def space_PiM)

lemma nonneg_cexpr_subset:
  assumes "nonneg_cexpr V Γ e" "V  V'" "free_vars e  V"
  shows "nonneg_cexpr V' Γ e"
proof (intro nonneg_cexprI)
  fix σ assume "σ  space (state_measure V' Γ)"
  with assms(2) have "restrict σ V  space (state_measure V Γ)"
    by (auto simp: state_measure_def space_PiM restrict_def)
  from nonneg_cexprD[OF assms(1) this] have "extract_real (cexpr_sem (restrict σ V) e)  0" .
  also have "cexpr_sem (restrict σ V) e = cexpr_sem σ e" using assms(3)
    by (intro cexpr_sem_eq_on_vars) auto
  finally show "extract_real (cexpr_sem σ e)  0" .
qed

lemma nonneg_cexpr_Mult:
  assumes "Γ c e1 : REAL" "Γ c e2 : REAL"
  assumes "free_vars e1  V" "free_vars e2  V"
  assumes N1: "nonneg_cexpr V Γ e1" and N2: "nonneg_cexpr V Γ e2"
  shows "nonneg_cexpr V Γ (e1 *c e2)"
proof (rule nonneg_cexprI)
  fix σ assume σ: "σ  space (state_measure V Γ)"
  hence "extract_real (cexpr_sem σ (e1 *c e2)) = extract_real (cexpr_sem σ e1) * extract_real (cexpr_sem σ e2)"
    using assms by (subst cexpr_sem_Mult[of Γ _ _ _ V]) simp_all
  also have "...  0" using σ N1 N2 by (intro mult_nonneg_nonneg nonneg_cexprD)
  finally show "extract_real (cexpr_sem σ (e1 *c e2))  0" .
qed

lemma nonneg_indicator:
  assumes "Γ c e : BOOL" "free_vars e  V"
  shows "nonneg_cexpr V Γ (ec)"
proof (intro nonneg_cexprI)
  fix ρ assume "ρ  space (state_measure V Γ)"
  with assms have "val_type (cexpr_sem ρ e) = BOOL" by (rule val_type_cexpr_sem)
  thus "extract_real (cexpr_sem ρ (ec))  0"
    by (auto simp: extract_real_def bool_to_real_def split: val.split)
qed

lemma nonneg_cexpr_comp_aux:
  assumes nonneg: "nonneg_cexpr V (Γ(x := t1)) e"  and x:"x  V"
  assumes t2: "Γ(x:=t1) c e : t2" and t1: "Γ c f : t1" and vars: "free_vars f  V"
  shows "nonneg_cexpr V Γ (cexpr_comp_aux x f e)"
proof (intro nonneg_cexprI)
  fix σ assume σ: "σ  space (state_measure V Γ)"
  have "extract_real (cexpr_sem σ (cexpr_comp_aux x f e)) =
            extract_real (cexpr_sem (σ(x := cexpr_sem σ f)) e)"
    by (simp add: cexpr_sem_cexpr_comp_aux)
  also from val_type_cexpr_sem[OF t1 vars σ] have "cexpr_sem σ f  type_universe t1" by auto
  with σ x have "σ(x := cexpr_sem σ f)  space (state_measure V (Γ(x := t1)))"
    by (auto simp: state_measure_def space_PiM shift_var_set_def split: if_split_asm)
  hence "extract_real (cexpr_sem (σ(x := cexpr_sem σ f)) e)  0"
    by(intro nonneg_cexprD[OF assms(1)])
  finally show "extract_real (cexpr_sem σ (cexpr_comp_aux x f e))  0" .
qed

lemma nonneg_cexpr_comp:
  assumes "nonneg_cexpr (shift_var_set V) (case_nat t2 Γ) e"
  assumes "case_nat t1 Γ c f : t2" "free_vars f  shift_var_set V"
  shows "nonneg_cexpr (shift_var_set V) (case_nat t1 Γ) (e c f)"
proof (intro nonneg_cexprI)
  fix σ assume σ: "σ  space (state_measure (shift_var_set V) (case_nat t1 Γ))"
  have "extract_real (cexpr_sem σ (e c f)) = extract_real (cexpr_sem (σ(0 := cexpr_sem σ f)) e)"
    by (simp add: cexpr_sem_cexpr_comp)
  also from val_type_cexpr_sem[OF assms(2,3) σ] have "cexpr_sem σ f  type_universe t2" by auto
  with σ have "σ(0 := cexpr_sem σ f)  space (state_measure (shift_var_set V) (case_nat t2 Γ))"
    by (auto simp: state_measure_def space_PiM shift_var_set_def split: if_split_asm)
  hence "extract_real (cexpr_sem (σ(0 := cexpr_sem σ f)) e)  0"
    by(intro nonneg_cexprD[OF assms(1)])
  finally show "extract_real (cexpr_sem σ (e c f))  0" .
qed

lemma nonneg_cexpr_subst_val:
  assumes "nonneg_cexpr (shift_var_set V) (case_nat t Γ) e" "val_type v = t"
  shows "nonneg_cexpr V Γ (cexpr_subst_val e v)"
proof (intro nonneg_cexprI)
  fix σ assume σ: "σ  space (state_measure V Γ)"
  moreover from assms(2) have "v  type_universe t" by auto
  ultimately show "extract_real (cexpr_sem σ (cexpr_subst_val e v))  0"
    by (auto intro!: nonneg_cexprD[OF assms(1)])
qed

lemma nonneg_cexpr_int:
  assumes "nonneg_cexpr (shift_var_set V) (case_nat t Γ) e"
  shows "nonneg_cexpr V Γ (c e t)"
proof (intro nonneg_cexprI)
  fix σ assume σ: "σ  space (state_measure V Γ)"
  have "extract_real (cexpr_sem σ (c e t)) = x. extract_real (cexpr_sem (case_nat x σ) e) stock_measure t"
    by (simp add: extract_real_def)
  also from σ have "...  0"
    by (intro integral_nonneg_AE AE_I2 nonneg_cexprD[OF assms]) auto
  finally show "extract_real (cexpr_sem σ (c e t))  0" .
qed


text ‹Subprobability density expressions›

definition "subprob_cexpr V V' Γ e 
    ρ  space (state_measure V' Γ).
      (+σ. extract_real (cexpr_sem (merge V V' (σ, ρ)) e) state_measure V Γ)  1"

lemma subprob_cexprI:
  assumes "ρ. ρ  space (state_measure V' Γ) 
                 (+σ. extract_real (cexpr_sem (merge V V' (σ, ρ)) e) state_measure V Γ)  1"
  shows "subprob_cexpr V V' Γ e"
  using assms unfolding subprob_cexpr_def by simp

lemma subprob_cexprD:
  assumes "subprob_cexpr V V' Γ e"
  shows "ρ. ρ  space (state_measure V' Γ) 
               (+σ. extract_real (cexpr_sem (merge V V' (σ, ρ)) e) state_measure V Γ)  1"
  using assms unfolding subprob_cexpr_def by simp

lemma subprob_indicator:
  assumes subprob: "subprob_cexpr V V' Γ e1" and nonneg: "nonneg_cexpr (V  V') Γ e1"
  assumes t1: "Γ c e1 : REAL" and t2: "Γ c e2 : BOOL"
  assumes vars1: "free_vars e1  V  V'" and vars2: "free_vars e2  V  V'"
  shows "subprob_cexpr V V' Γ (e1 *c e2c)"
proof (intro subprob_cexprI)
  fix ρ assume ρ: "ρ  space (state_measure V' Γ)"
  from t2 have t2': "Γ c e2c : REAL" by (rule cet_op) simp_all
  from vars2 have vars2': "free_vars (e2c)  V  V'" by simp
  let ?eval = "λσ e. extract_real (cexpr_sem (merge V V' (σ, ρ)) e)"
  have "(+σ. ?eval σ (e1 *c e2c) state_measure V Γ) =
            (+σ. ?eval σ e1 * ?eval σ (e2c) state_measure V Γ)"
    by (intro nn_integral_cong)
       (simp only: cexpr_sem_Mult[OF t1 t2' merge_in_state_measure[OF _ ρ] vars1 vars2'])
  also {
    fix σ assume σ: "σ  space (state_measure V Γ)"
    with ρ have "val_type (cexpr_sem (merge V V' (σ,ρ)) e2) = BOOL"
      by (intro val_type_cexpr_sem[OF t2 vars2] merge_in_state_measure)
    hence "?eval σ (e2c)  {0,1}"
      by (cases "cexpr_sem (merge V V' (σ,ρ)) e2") (auto simp: extract_real_def bool_to_real_def)
    moreover have "?eval σ e1  0" using nonneg ρ σ
      by (auto intro!: nonneg_cexprD merge_in_state_measure)
    ultimately have "?eval σ e1 * ?eval σ (e2c)  ?eval σ e1"
      by (intro mult_right_le_one_le) auto
  }
  hence "(+σ. ?eval σ e1 * ?eval σ (e2c) state_measure V Γ) 
             (+σ. ?eval σ e1 state_measure V Γ)"
    by (intro nn_integral_mono) (simp add: ennreal_leI)
  also from subprob and ρ have "...  1" by (rule subprob_cexprD)
  finally show "(+σ. ?eval σ (e1 *c e2c) state_measure V Γ)   1" .
qed

lemma measurable_cexpr_sem':
  assumes ρ: "ρ  space (state_measure V' Γ)"
  assumes e: "Γ c e : REAL" "free_vars e  V  V'"
  shows "(λσ. extract_real (cexpr_sem (merge V V' (σ, ρ)) e))
             borel_measurable (state_measure V Γ)"
  apply (rule measurable_compose[OF _ measurable_extract_real])
  apply (rule measurable_compose[OF _ measurable_cexpr_sem[OF e]])
  apply (insert ρ, unfold state_measure_def, rule measurable_compose[OF _ measurable_merge], simp)
  done

lemma measurable_fun_upd_state_measure[measurable]:
  assumes "v  V"
  shows "(λ(x,y). y(v := x))  measurable (stock_measure (Γ v) M state_measure V Γ)
                                          (state_measure (insert v V) Γ)"
  unfolding state_measure_def by simp


lemma integrable_cexpr_projection:
  assumes fin: "finite V"
  assumes disjoint: "V  V' = {}" "v  V" "v  V'"
  assumes ρ: "ρ  space (state_measure V' Γ)"
  assumes e: "Γ c e : REAL" "free_vars e  insert v V  V'"
  assumes int: "integrable (state_measure (insert v V) Γ)
                    (λσ. extract_real (cexpr_sem (merge (insert v V) V' (σ, ρ)) e))"
                (is "integrable _ ?f'")
  shows "AE x in stock_measure (Γ v).
           integrable (state_measure V Γ)
               (λσ. extract_real (cexpr_sem (merge V (insert v V') (σ, ρ(v := x))) e))"
    (is "AE x in ?N. integrable ?M (?f x)")
proof (unfold real_integrable_def, intro AE_conjI)
  show "AE x in ?N. ?f x  borel_measurable ?M" using ρ e disjoint
    by (intro AE_I2 measurable_cexpr_sem')
       (auto simp: state_measure_def space_PiM dest: PiE_mem split: if_split_asm)

  let ?f'' = "λx σ. extract_real (cexpr_sem (merge (insert v V) V' (σ(v := x), ρ)) e)"
  {
    fix x σ assume "x  space ?N" "σ  space ?M"
    hence "merge (insert v V) V' (σ(v := x), ρ) = merge V (insert v V') (σ, ρ(v := x))"
      using disjoint by (intro ext) (simp add: merge_def split: if_split_asm)
    hence "?f'' x σ = ?f x σ" by simp
  } note f''_eq_f = this

  interpret product_sigma_finite "(λv. stock_measure (Γ v))"
    by (simp add: product_sigma_finite_def)
  interpret sigma_finite_measure "state_measure V Γ"
    by (rule sigma_finite_state_measure[OF fin])

  from int have "(+σ. ennreal (?f' σ) state_measure (insert v V) Γ)  "
    by (simp add: real_integrable_def)
  also have "(+σ. ennreal (?f' σ) state_measure (insert v V) Γ) =
                 +x. +σ. ennreal (?f'' x σ) ?M ?N" (is "_ = ?I")
    using fin disjoint e ρ
    by (unfold state_measure_def, subst product_nn_integral_insert_rev)
       (auto intro!: measurable_compose[OF _ measurable_ennreal] measurable_cexpr_sem'[unfolded state_measure_def])
  finally have "AE x in ?N. (+σ. ennreal (?f'' x σ) ?M)  " (is ?P) using e disjoint
    by (intro nn_integral_PInf_AE)
       (auto simp: measurable_split_conv intro!: borel_measurable_nn_integral measurable_compose[OF _ measurable_ennreal]
                   measurable_compose[OF _ measurable_cexpr_sem'[OF ρ]])
  moreover have "x. x  space ?N  (+σ. ennreal (?f'' x σ) ?M) = (+σ. ennreal (?f x σ) ?M)"
    by (intro nn_integral_cong) (simp add: f''_eq_f)
  hence "?P  (AE x in ?N. (+σ. ennreal (?f x σ) ?M)  )" by (intro AE_cong) simp
  ultimately show "AE x in ?N. (+σ. ennreal (?f x σ) ?M)  " by simp

  from int have "(+σ. ennreal (-?f' σ) state_measure (insert v V) Γ)  "
    by (simp add: real_integrable_def)
  also have "(+σ. ennreal (-?f' σ) state_measure (insert v V) Γ) =
                 +x. +σ. ennreal (-?f'' x σ) ?M ?N" (is "_ = ?I")
    using fin disjoint e ρ
    by (unfold state_measure_def, subst product_nn_integral_insert_rev)
       (auto intro!: measurable_compose[OF _ measurable_ennreal] borel_measurable_uminus
                     measurable_cexpr_sem'[unfolded state_measure_def])
  finally have "AE x in ?N. (+σ. ennreal (-?f'' x σ) ?M)  " (is ?P) using e disjoint
    by (intro nn_integral_PInf_AE)
       (auto simp: measurable_split_conv intro!: borel_measurable_nn_integral measurable_compose[OF _ measurable_ennreal]
                   measurable_compose[OF _ measurable_cexpr_sem'[OF ρ]] borel_measurable_uminus)
  moreover have "x. x  space ?N  (+σ. ennreal (-?f'' x σ) ?M) = (+σ. ennreal (-?f x σ) ?M)"
    by (intro nn_integral_cong) (simp add: f''_eq_f)
  hence "?P  (AE x in ?N. (+σ. ennreal (-?f x σ) ?M)  )" by (intro AE_cong) simp
  ultimately show "AE x in ?N. (+σ. ennreal (-?f x σ) ?M)  " by simp
qed


definition cdens_ctxt_invar :: "vname list  vname list  tyenv  cexpr  bool" where
  "cdens_ctxt_invar vs vs' Γ δ 
       distinct (vs @ vs') 
       free_vars δ  set (vs @ vs') 
       Γ c δ : REAL 
       nonneg_cexpr (set vs  set vs') Γ δ 
       subprob_cexpr (set vs) (set vs') Γ δ"

lemma cdens_ctxt_invarI:
  "distinct (vs @ vs'); free_vars δ  set (vs @ vs'); Γ c δ : REAL;
    nonneg_cexpr (set vs  set vs') Γ δ;
    subprob_cexpr (set vs) (set vs') Γ δ  
      cdens_ctxt_invar vs vs' Γ δ"
  by (simp add: cdens_ctxt_invar_def)

lemma cdens_ctxt_invarD:
  assumes "cdens_ctxt_invar vs vs' Γ δ"
  shows "distinct (vs @ vs')" "free_vars δ  set (vs @ vs')" "Γ c δ : REAL"
        "nonneg_cexpr (set vs  set vs') Γ δ" "subprob_cexpr (set vs) (set vs') Γ δ"
  using assms by (simp_all add: cdens_ctxt_invar_def)

lemma cdens_ctxt_invar_empty:
  assumes "cdens_ctxt_invar vs vs' Γ δ"
  shows "cdens_ctxt_invar [] (vs @ vs') Γ (CReal 1)"
  using cdens_ctxt_invarD[OF assms]
  by (intro cdens_ctxt_invarI)
     (auto simp: cexpr_type_Some_iff[symmetric] extract_real_def state_measure_def PiM_empty
           intro!: nonneg_cexprI subprob_cexprI)

lemma cdens_ctxt_invar_imp_integrable:
  assumes "cdens_ctxt_invar vs vs' Γ δ" and ρ: "ρ  space (state_measure (set vs') Γ)"
  shows "integrable (state_measure (set vs) Γ)
             (λσ. extract_real (cexpr_sem (merge (set vs) (set vs') (σ, ρ)) δ))" (is "integrable ?M ?f")
  unfolding integrable_iff_bounded
proof (intro conjI)
  note invar = cdens_ctxt_invarD[OF assms(1)]
  show "?f  borel_measurable ?M"
    apply (rule measurable_compose[OF _ measurable_extract_real])
    apply (rule measurable_compose[OF _ measurable_cexpr_sem[OF invar(3,2)]])
    apply (simp only: state_measure_def set_append, rule measurable_compose[OF _ measurable_merge])
    apply (rule measurable_Pair, simp, insert assms(2), simp add: state_measure_def)
    done

  have nonneg: "σ. σ  space ?M  ?f σ  0"
    using nonneg_cexpr (set vs  set vs') Γ δ
    by (rule nonneg_cexprD, intro merge_in_state_measure[OF _ ρ])
  with subprob_cexpr (set vs) (set vs') Γ δ and ρ
  show "(+σ. ennreal (norm (?f σ)) ?M) < " unfolding subprob_cexpr_def
    by (auto simp: less_top[symmetric] top_unique cong: nn_integral_cong)
qed


subsection ‹Randomfree expressions›

text ‹
  Translates an expression with no occurrences of Random or Fail into an
  equivalent target language expression.
›
primrec expr_rf_to_cexpr :: "expr  cexpr" where
  "expr_rf_to_cexpr (Val v) = CVal v"
| "expr_rf_to_cexpr (Var x) = CVar x"
| "expr_rf_to_cexpr <e1, e2> = <expr_rf_to_cexpr e1, expr_rf_to_cexpr e2>c"
| "expr_rf_to_cexpr (oper $$ e) = oper $$c (expr_rf_to_cexpr e)"
| "expr_rf_to_cexpr (IF b THEN e1 ELSE e2) =
      (IFc expr_rf_to_cexpr b THEN expr_rf_to_cexpr e1 ELSE expr_rf_to_cexpr e2)"
| "expr_rf_to_cexpr (LET e1 IN e2) =
      cexpr_subst 0 (expr_rf_to_cexpr e1) (expr_rf_to_cexpr e2)"
| "expr_rf_to_cexpr (Random _ _) = undefined"
| "expr_rf_to_cexpr (Fail _) = undefined"

lemma cexpr_sem_expr_rf_to_cexpr:
     "randomfree e  cexpr_sem σ (expr_rf_to_cexpr e) = expr_sem_rf σ e"
  by (induction e arbitrary: σ) (auto simp: cexpr_sem_cexpr_subst)

lemma cexpr_typing_expr_rf_to_cexpr[intro]:
    assumes "Γ  e : t" "randomfree e"
    shows "Γ c expr_rf_to_cexpr e : t"
  using assms by (induction rule: expr_typing.induct) (auto intro!: cexpr_typing.intros)

lemma free_vars_expr_rf_to_cexpr:
  "randomfree e  free_vars (expr_rf_to_cexpr e)  free_vars e"
proof (induction e)
  case (LetVar e1 e2)
  thus ?case
    by (simp only: free_vars_cexpr.simps expr_rf_to_cexpr.simps,
        intro order.trans[OF free_vars_cexpr_subst]) auto
qed auto

subsection ‹Builtin density expressions›

primrec dist_dens_cexpr :: "pdf_dist  cexpr  cexpr  cexpr" where
  "dist_dens_cexpr Bernoulli p x = (IFc CReal 0 c p c p c CReal 1 THEN
                                       IFc x THEN p ELSE CReal 1 -c p
                                    ELSE CReal 0)"
| "dist_dens_cexpr UniformInt p x = (IFc fstc p c sndc p c fstc p c x c x c sndc p THEN
                                         inversec (sndc p -c fstc p +c CInt 1c) ELSE CReal 0)"
| "dist_dens_cexpr UniformReal p x = (IFc fstc p <c sndc p c fstc p c x c x c sndc p THEN
                                         inversec (sndc p -c fstc p) ELSE CReal 0)"
| "dist_dens_cexpr Gaussian p x = (IFc CReal 0 <c sndc p THEN
                                     expc (-c((x -c fstc p)^cCInt 2 /c (CReal 2 *c sndc p^cCInt 2))) /c
                                         sqrtc (CReal 2 *c πc *c sndc p ^c CInt 2) ELSE CReal 0)"
| "dist_dens_cexpr Poisson p x = (IFc CReal 0 <c p c CInt 0 c x THEN
                                    p ^c x /c factc xc *c expc (-c p) ELSE CReal 0)"

lemma free_vars_dist_dens_cexpr:
    "free_vars (dist_dens_cexpr dst e1 e2)  free_vars e1  free_vars e2"
  by (subst dist_dens_cexpr_def, cases dst) simp_all

lemma cexpr_typing_dist_dens_cexpr:
    assumes "Γ c e1 : dist_param_type dst" "Γ c e2 : dist_result_type dst"
    shows "Γ c dist_dens_cexpr dst e1 e2 : REAL"
  using assms
  apply (subst dist_dens_cexpr_def, cases dst)
  (* Bernoulli *)
  apply (simp, intro cet_op_intros cet_if cet_val' cet_var' cet_eq, simp_all) []
  (* Uniform int *)
  apply (simp, intro cet_if cet_and cet_or cet_less_int cet_eq)
  apply (erule cet_fst cet_snd | simp)+
  apply (rule cet_inverse, rule cet_op[where t = INTEG], intro cet_add_int cet_minus_int)
  apply (simp_all add: cet_val' cet_fst cet_snd) [5]
  (* Uniform real *)
  apply (simp, intro cet_if cet_op_intros cet_eq cet_fst cet_snd, simp_all add: cet_val') []
  (* Poisson *)
  apply (simp, intro cet_if cet_and, rule cet_less_real, simp add: cet_val', simp)
  apply (rule cet_less_eq_int, simp add: cet_val', simp)
  apply (intro cet_mult_real cet_pow_real cet_inverse cet_cast_real_int cet_exp cet_minus_real
               cet_op[where oper = Fact and t = INTEG] cet_var', simp_all add: cet_val') [2]
  (* Gaussian *)
  apply (simp, intro cet_if cet_op_intros cet_val', simp_all add: cet_fst cet_snd)
  done


lemma val_type_eq_BOOL: "val_type x = BOOL  x  BoolVal`UNIV"
  by (cases x) auto

lemma val_type_eq_INTEG: "val_type x = INTEG  x  IntVal`UNIV"
  by (cases x) auto

lemma val_type_eq_PRODUCT: "val_type x = PRODUCT t1 t2 
  (a b. val_type a = t1  val_type b = t2  x = <| a, b |>)"
  by (cases x) auto

lemma cexpr_sem_dist_dens_cexpr_nonneg:
  assumes "Γ c e1 : dist_param_type dst" "Γ c e2 : dist_result_type dst"
  assumes "free_vars e1  V" "free_vars e2  V"
  assumes "σ  space (state_measure V Γ)"
  shows "ennreal (extract_real (cexpr_sem σ (dist_dens_cexpr dst e1 e2))) =
           dist_dens dst (cexpr_sem σ e1) (cexpr_sem σ e2) 
           0  extract_real (cexpr_sem σ (dist_dens_cexpr dst e1 e2))"
proof-
  from val_type_cexpr_sem[OF assms(1,3,5)] and val_type_cexpr_sem[OF assms(2,4,5)]
    have "cexpr_sem σ e1  space (stock_measure (dist_param_type dst))" and
         "cexpr_sem σ e2  space (stock_measure (dist_result_type dst))"
    by (auto simp: type_universe_def simp del: type_universe_type)
  thus ?thesis
    by (subst dist_dens_cexpr_def, cases dst)
       (auto simp:
            lift_Comp_def lift_RealVal_def lift_RealIntVal_def lift_RealIntVal2_def
            bernoulli_density_def val_type_eq_REAL val_type_eq_BOOL val_type_eq_PRODUCT val_type_eq_INTEG
            uniform_int_density_def uniform_real_density_def
            lift_IntVal_def poisson_density'_def one_ennreal_def
            field_simps gaussian_density_def)
qed

lemma cexpr_sem_dist_dens_cexpr:
  assumes "Γ c e1 : dist_param_type dst" "Γ c e2 : dist_result_type dst"
  assumes "free_vars e1  V" "free_vars e2  V"
  assumes "σ  space (state_measure V Γ)"
  shows "ennreal (extract_real (cexpr_sem σ (dist_dens_cexpr dst e1 e2))) =
           dist_dens dst (cexpr_sem σ e1) (cexpr_sem σ e2)"
  using cexpr_sem_dist_dens_cexpr_nonneg[OF assms] by simp

lemma nonneg_dist_dens_cexpr:
  assumes "Γ c e1 : dist_param_type dst" "Γ c e2 : dist_result_type dst"
  assumes "free_vars e1  V" "free_vars e2  V"
  shows "nonneg_cexpr V Γ (dist_dens_cexpr dst e1 e2)"
proof (intro nonneg_cexprI)
  fix σ assume ρ: "σ  space (state_measure V Γ)"
  from cexpr_sem_dist_dens_cexpr_nonneg[OF assms this]
  show "0  extract_real (cexpr_sem σ (dist_dens_cexpr dst e1 e2))"
    by simp
qed

subsection ‹Integral expressions›

definition integrate_var :: "tyenv  vname  cexpr  cexpr" where
  "integrate_var Γ v e = c map_vars (λw. if v = w then 0 else Suc w) e (Γ v)"

definition integrate_vars :: "tyenv  vname list  cexpr  cexpr" where
  "integrate_vars Γ = foldr (integrate_var Γ)"

lemma cexpr_sem_integrate_var:
  "cexpr_sem σ (integrate_var Γ v e) =
    RealVal (x. extract_real (cexpr_sem (σ(v := x)) e) stock_measure (Γ v))"
proof-
  let ?f = "(λw. if v = w then 0 else Suc w)"
  have "cexpr_sem σ (integrate_var Γ v e) =
          RealVal (x. extract_real (cexpr_sem (case_nat x σ  ?f) e) stock_measure (Γ v))"
    by (simp add: extract_real_def integrate_var_def cexpr_sem_map_vars)
  also have "(λx. case_nat x σ  ?f) = (λx. σ(v := x))"
    by (intro ext) (simp add: o_def split: if_split)
  finally show ?thesis .
qed

lemma cexpr_sem_integrate_var':
  "extract_real (cexpr_sem σ (integrate_var Γ v e)) =
      (x. extract_real (cexpr_sem (σ(v := x)) e) stock_measure (Γ v))"
  by (subst cexpr_sem_integrate_var, simp add: extract_real_def)

lemma cexpr_typing_integrate_var[simp]:
    "Γ c e : REAL  Γ c integrate_var Γ v e : REAL"
  unfolding integrate_var_def
  by (rule cexpr_typing.intros, rule cexpr_typing_map_vars)
     (erule cexpr_typing_cong', simp split: nat.split)

lemma cexpr_typing_integrate_vars[simp]:
    "Γ c e : REAL  Γ c integrate_vars Γ vs e : REAL"
  by (induction vs arbitrary: e)
     (simp_all add: integrate_vars_def)

lemma free_vars_integrate_var[simp]:
    "free_vars (integrate_var Γ v e) = free_vars e - {v}"
  by (auto simp: integrate_var_def)

lemma free_vars_integrate_vars[simp]:
    "free_vars (integrate_vars Γ vs e) = free_vars e - set vs"
  by (induction vs arbitrary: e) (auto simp: integrate_vars_def)

lemma (in product_sigma_finite) product_integral_insert':
  fixes f :: "_  real"
  assumes "finite I" "i  I" "integrable (PiM (insert i I) M) f"
  shows "integralL (PiM (insert i I) M) f = LINT y|M i. LINT x|PiM I M. f (x(i := y))"
proof-
  interpret pair_sigma_finite "M i" "PiM I M"
    by (simp_all add: sigma_finite assms pair_sigma_finite_def sigma_finite_measures)
  interpret Mi: sigma_finite_measure "M i"
    by (simp add: assms sigma_finite_measures)
  from assms(3) have int: "integrable (M i M PiM I M) (λ(x, y). f (y(i := x)))"
  unfolding real_integrable_def
    apply (elim conjE)
    apply (subst (1 2) nn_integral_snd[symmetric])
    apply ((subst (asm) (1 2) product_nn_integral_insert[OF assms(1,2)],
           auto intro!: measurable_compose[OF _ measurable_ennreal] borel_measurable_uminus) [])+
    done
  from assms have "integralL (PiM (insert i I) M) f = LINT x|PiM I M. LINT y|M i. f (x(i := y))"
    by (rule product_integral_insert)
  also from int have "... = LINT y|M i. LINT x|PiM I M. f (x(i := y))"
    by (rule Fubini_integral)
  finally show ?thesis .
qed


lemma cexpr_sem_integrate_vars:
  assumes ρ: "ρ  space (state_measure V' Γ)"
  assumes disjoint: "distinct vs" "set vs  V' = {}"
  assumes "integrable (state_measure (set vs) Γ)
               (λσ. extract_real (cexpr_sem (merge (set vs) V' (σ, ρ)) e))"
  assumes e: "Γ c e : REAL" "free_vars e  set vs  V'"
  shows "extract_real (cexpr_sem ρ (integrate_vars Γ vs e)) =
           σ. extract_real (cexpr_sem (merge (set vs) V' (σ, ρ)) e) state_measure (set vs) Γ"
using assms
proof (induction vs arbitrary: ρ V')
  case Nil
  hence "v. (if v  V' then ρ v else undefined) = ρ v"
    by (auto simp: state_measure_def space_PiM)
  thus ?case by (auto simp: integrate_vars_def state_measure_def merge_def PiM_empty)
next
  case (Cons v vs ρ V')
  interpret product_sigma_finite "λv. stock_measure (Γ v)"
    by (simp add: product_sigma_finite_def)
  interpret sigma_finite_measure "state_measure (set vs) Γ"
    by (simp add: sigma_finite_state_measure)
  have ρ': "x. x  type_universe (Γ v)  ρ(v := x)  space (state_measure (insert v V') Γ)"
    using Cons.prems(1) by (auto simp: state_measure_def space_PiM split: if_split_asm)
  have "extract_real (cexpr_sem ρ (integrate_vars Γ (v # vs) e)) =
          x. extract_real (cexpr_sem (ρ(v := x)) (integrate_vars Γ vs e)) stock_measure (Γ v)"
    (is "_ = ?I") by (simp add: integrate_vars_def cexpr_sem_integrate_var extract_real_def)
  also from Cons.prems(4) have int: "integrable (state_measure (insert v (set vs)) Γ)
      (λσ. extract_real (cexpr_sem (merge (insert v (set vs)) V' (σ, ρ)) e))" by simp
  have "AE x in stock_measure (Γ v).
                extract_real (cexpr_sem (ρ(v := x)) (integrate_vars Γ vs e)) =
                  σ. extract_real (cexpr_sem (merge (set vs) (insert v V') (σ, ρ(v := x))) e)
                      state_measure (set vs) Γ"
    apply (rule AE_mp[OF _ AE_I2[OF impI]])
    apply (rule integrable_cexpr_projection[OF _ _ _ _ _ _ _ int])
    apply (insert Cons.prems, auto) [7]
    apply (subst Cons.IH, rule ρ', insert Cons.prems, auto)
    done
  hence "?I = x. σ. extract_real (cexpr_sem (merge (set vs) (insert v V') (σ, ρ(v := x))) e)
                  state_measure (set vs) Γ stock_measure (Γ v)" using Cons.prems
    apply (intro integral_cong_AE)
    apply (rule measurable_compose[OF measurable_Pair_compose_split[OF
                measurable_fun_upd_state_measure[of v V' Γ]]])
    apply (simp, simp, simp, rule measurable_compose[OF _ measurable_extract_real])
    apply (rule measurable_cexpr_sem, simp, (auto) [])
    apply (rule borel_measurable_lebesgue_integral)
    apply (subst measurable_split_conv)
    apply (rule measurable_compose[OF _ measurable_extract_real])
    apply (rule measurable_compose[OF _ measurable_cexpr_sem[of Γ _ _  "set vs  insert v V'"]])
    apply (unfold state_measure_def, rule measurable_compose[OF _ measurable_merge])
    apply simp_all
    done
  also have "(λx σ. merge (set vs) (insert v V') (σ, ρ(v := x))) =
                 (λx σ. merge (set (v#vs)) V' (σ(v := x), ρ))"
    using Cons.prems by (intro ext) (auto simp: merge_def split: if_split)
  also have "(x. σ. extract_real (cexpr_sem (merge (set (v#vs)) V' (σ(v := x), ρ)) e)
                  state_measure (set vs) Γ stock_measure (Γ v)) =
               σ. extract_real (cexpr_sem (merge (set (v#vs)) V' (σ, ρ)) e)
                  state_measure (set (v#vs)) Γ"
    using Cons.prems unfolding state_measure_def
    by (subst (2) set_simps, subst product_integral_insert') simp_all
  finally show ?case .
qed

lemma cexpr_sem_integrate_vars':
  assumes ρ: "ρ  space (state_measure V' Γ)"
  assumes disjoint: "distinct vs" "set vs  V' = {}"
  assumes nonneg: "nonneg_cexpr (set vs  V') Γ e"
  assumes "integrable (state_measure (set vs) Γ)
               (λσ. extract_real (cexpr_sem (merge (set vs) V' (σ, ρ)) e))"
  assumes e: "Γ c e : REAL" "free_vars e  set vs  V'"
  shows "ennreal (extract_real (cexpr_sem ρ (integrate_vars Γ vs e))) =
           +σ. extract_real (cexpr_sem (merge (set vs) V' (σ, ρ)) e) state_measure (set vs) Γ"
proof-
  from assms have "extract_real (cexpr_sem ρ (integrate_vars Γ vs e)) =
      σ. extract_real (cexpr_sem (merge (set vs) V' (σ, ρ)) e) state_measure (set vs) Γ"
    by (intro cexpr_sem_integrate_vars)
  also have "ennreal ... =
      +σ. extract_real (cexpr_sem (merge (set vs) V' (σ, ρ)) e) state_measure (set vs) Γ"
    using assms
    by (intro nn_integral_eq_integral[symmetric] AE_I2)
       (auto intro!: nonneg_cexprD merge_in_state_measure)
  finally show ?thesis .
qed

lemma nonneg_cexpr_sem_integrate_vars:
  assumes ρ: "ρ  space (state_measure V' Γ)"
  assumes disjoint: "distinct vs" "set vs  V' = {}"
  assumes nonneg: "nonneg_cexpr (set vs  V') Γ e"
  assumes e: "Γ c e : REAL" "free_vars e  set vs  V'"
  shows "extract_real (cexpr_sem ρ (integrate_vars Γ vs e))  0"
using assms
proof (induction vs arbitrary: ρ V')
  case Nil
  hence "v. (if v  V' then ρ v else undefined) = ρ v"
    by (auto simp: state_measure_def space_PiM)
  with Nil show ?case
    by (auto simp: integrate_vars_def state_measure_def merge_def PiM_empty nonneg_cexprD)
next
  case (Cons v vs ρ V')
  have ρ': "x. x  type_universe (Γ v)  ρ(v := x)  space (state_measure (insert v V') Γ)"
    using Cons.prems(1) by (auto simp: state_measure_def space_PiM split: if_split_asm)
  have "extract_real (cexpr_sem ρ (integrate_vars Γ (v # vs) e)) =
          x. extract_real (cexpr_sem (ρ(v := x)) (integrate_vars Γ vs e)) stock_measure (Γ v)"
    by (simp add: integrate_vars_def cexpr_sem_integrate_var extract_real_def)
  also have "...  0"
    by (rule integral_nonneg_AE, rule AE_I2, subst Cons.IH[OF ρ']) (insert Cons.prems, auto)
  finally show "extract_real (cexpr_sem ρ (integrate_vars Γ (v # vs) e))  0" .
qed

lemma nonneg_cexpr_sem_integrate_vars':
  "distinct vs  set vs  V' = {}  nonneg_cexpr (set vs  V') Γ e  Γ c e : REAL 
    free_vars e  set vs  V'  nonneg_cexpr V' Γ (integrate_vars Γ vs e)"
  apply (intro nonneg_cexprI allI)
  apply (rule nonneg_cexpr_sem_integrate_vars[where V'=V'])
  apply auto
  done

lemma cexpr_sem_integral_nonneg:
  assumes finite: "(+x. extract_real (cexpr_sem (case_nat x σ) e) stock_measure t) < "
  assumes nonneg: "nonneg_cexpr (shift_var_set V) (case_nat t Γ) e"
  assumes t: "case_nat t Γ c e : REAL" and vars: "free_vars e  shift_var_set V"
  assumes ρ: "σ  space (state_measure V Γ)"
  shows "ennreal (extract_real (cexpr_sem σ (c e t))) =
             +x. extract_real (cexpr_sem (case_nat x σ) e) stock_measure t"
proof-
  let ?f = "λx. extract_real (cexpr_sem (case_nat x σ) e)"
  have meas: "?f  borel_measurable (stock_measure t)"
    apply (rule measurable_compose[OF _ measurable_extract_real])
    apply (rule measurable_compose[OF measurable_case_nat' measurable_cexpr_sem])
    apply (rule measurable_ident_sets[OF refl], rule measurable_const[OF ρ])
    apply (simp_all add: t vars)
    done
  from this and finite and nonneg have int: "integrable (stock_measure t) ?f"
    by (auto intro!: integrableI_nonneg nonneg_cexprD case_nat_in_state_measure[OF _ ρ])

  have "extract_real (cexpr_sem σ (c e t)) =
          x. extract_real (cexpr_sem (case_nat x σ) e) stock_measure t"
    by (simp add: extract_real_def)
  also have "ennreal ... = +x. extract_real (cexpr_sem (case_nat x σ) e) stock_measure t"
    by (subst nn_integral_eq_integral[OF int AE_I2])
       (auto intro!: nonneg_cexprD[OF nonneg] case_nat_in_state_measure[OF _ ρ])
  finally show ?thesis .
qed

lemma has_parametrized_subprob_density_cexpr_sem_integral:
  assumes dens: "has_parametrized_subprob_density (state_measure V' Γ) M (stock_measure t)
                   (λρ x. +y. eval_cexpr f (case_nat x ρ) y stock_measure t')"
  assumes nonneg: "nonneg_cexpr (shift_var_set (shift_var_set V')) (case_nat t' (case_nat t Γ)) f"
  assumes tf: "case_nat t' (case_nat t Γ) c f : REAL"
  assumes varsf: "free_vars f  shift_var_set (shift_var_set V')"
  assumes ρ: "ρ  space (state_measure V' Γ)"
  shows "AE x in stock_measure t.
          (+y. eval_cexpr f (case_nat x ρ) y stock_measure t') = ennreal (eval_cexpr (c f t') ρ x)"
proof (rule AE_mp[OF _ AE_I2[OF impI]])
  interpret sigma_finite_measure "stock_measure t'" by simp
  let ?f = "λx. +y. eval_cexpr f (case_nat x ρ) y stock_measure t'"
  from has_parametrized_subprob_density_integral[OF dens ρ]
    have "(+x. ?f x stock_measure t)  " by (auto simp: eval_cexpr_def top_unique)
  thus "AE x in stock_measure t. ?f x  " using ρ tf varsf by (intro nn_integral_PInf_AE) simp_all
  fix x assume x: "x  space (stock_measure t)" and finite: "?f x  "
  have nonneg': "AE y in stock_measure t'. eval_cexpr f (case_nat x ρ) y  0"
    unfolding eval_cexpr_def using ρ x
    by (intro AE_I2 nonneg_cexprD[OF nonneg]) (auto intro!: case_nat_in_state_measure)
  hence "integrable (stock_measure t') (λy. eval_cexpr f (case_nat x ρ) y)"
    using x ρ tf varsf finite by (intro integrableI_nonneg) (simp_all add: top_unique less_top)
  thus "?f x = ennreal (eval_cexpr (c f t') ρ x)" using nonneg'
    by (simp add: extract_real_def nn_integral_eq_integral eval_cexpr_def)
qed

end