Theory KZG_hiding

theory KZG_hiding

imports KZG_correct DL_assumption tDL_assumption CryptHOL_ext
begin

locale KZG_PCS_weak_hiding = KZG_PCS_correct
begin

section ‹Hiding of the KZG›

subsection ‹Definitions for the weak hiding game›

text ‹The difference between the weak hiding game and the hiding game is that in the weak hiding 
game the polynomial is uniform random, not arbitrary. Since arbitrary values can be 
modelled as uninitialized parameters in Isabelle, like the polynomial in the hiding game, we can make
the hiding weaker by instantiating a part of it.›

text ‹The polynomial is chosen uniform random. We also enforce two additional properties on the 
adversary outputs. With this construction we can use the predefined eval hiding game from 
Polynomial Commitment Scheme.›
definition weak_eval_hiding_adversary1 :: "('a vk, 'e argument,'state) 
  eval_hiding_adversary1  ('a vk, 'e argument,'state) eval_hiding_adversary1"
  where 
  "weak_eval_hiding_adversary1 𝒜 vk = do {
    (I,σ)  𝒜 vk;
    _ :: unit  assert_spmf (length I = max_deg  distinct I);
    return_spmf (I,σ)
    }"

text ‹eval\_hiding\_game except the polynomial is chosen at uniform random and not by the adversary.›
definition weak_eval_hiding_game ::  "('a vk, 'e argument, 'state) eval_hiding_adversary1  
  ('e mod_ring, 'a vk, 'a commit, 'e argument, 'e evaluation, 'a witness, 'state) 
  eval_hiding_adversary2  bool spmf"
  where "weak_eval_hiding_game 𝒜1 𝒜2 = 
  TRY do {
    p::'e mod_ring poly  sample_uniform_poly max_deg;
    eval_hiding_game p (weak_eval_hiding_adversary1 𝒜1) 𝒜2 
  } ELSE return_spmf False"

text ‹The advantage of the adversary over the weak hiding game is the advantage of eval hiding with
the weak adversary.›
definition weak_eval_hiding_advantage :: "('a vk, 'e argument, 'state) eval_hiding_adversary1  
  ('e mod_ring, 'a vk, 'a commit, 'e argument, 'e evaluation, 'a witness, 'state) 
  eval_hiding_adversary2  real"
  where "weak_eval_hiding_advantage 𝒜1 𝒜2 
     spmf (weak_eval_hiding_game 𝒜1 𝒜2) True"

subsubsection ‹DL game›

text ‹We instantiate the DL game for the group Gp›
sublocale DL_Gp: DL Gp "of_int_mod_ring  int" "pow_mod_ring Gp"
  unfolding DL_def 
  by (rule Gp.cyclic_group_axioms)

text ‹We instantiate the t-DL game for the group Gp›

sublocale t_DL_Gp: t_DL Gp max_deg "of_int_mod_ring  int" "pow_mod_ring Gp"
  unfolding t_DL_def 
  by (rule Gp.cyclic_group_axioms)

subsubsection ‹Reduction›

text ‹interpolate a polynomial over group points i.e. points of the form (i,g\textasciicircum{}i). 
Furthermore, return the polynomial evaluated at a value α›
definition interpolate_on :: "('e mod_ring × 'a) list  'e mod_ring  'a" where
  "interpolate_on xs_ys α= do {
  let xs = map fst xs_ys;
  let lagrange_exp = map (λ(xj,yj). yj ^Gp(poly (lagrange_basis_poly xs xj) α)) xs_ys;
  fold (λx prod. prod Gpx) lagrange_exp 𝟭}"

text ‹filter for the elements that are in xs but not in ys›
fun filter_distinct :: "'e mod_ring list  'e argument list  'e argument list"
  where "filter_distinct xs ys = filter (λx. find ((=) x) ys = None) xs"

lemma set_filter_distinct: "set (filter_distinct xs ys) = {x. x  set xs  x  set ys}"
  by (simp add: find_None_iff)

lemma length_filter_distinct:
  assumes "card (set xs) > card (set ys)"
  shows "length (filter_distinct xs ys) > 0"
proof -
  have "{x. x  set xs  x  set ys} = set xs - set ys"
    by blast
  moreover have "card (set xs - set ys)  card (set xs) - card (set ys)"
    using diff_card_le_card_Diff by blast
  moreover have " > 0"
    using assms calculation(2) by linarith
  ultimately show ?thesis
    using set_filter_distinct 
    by (metis bot_nat_0.extremum_uniqueI card_length not_gr_zero)
qed

text ‹function to pick a field element that is not in I. Intuitively, this function 
returns the lowest value not contained in I (note, finite fields do not have a ordering by 
default).›
fun PickDistinct :: "'e argument list  'e argument"
  where "PickDistinct I = hd (filter_distinct (map (of_int_mod_ring::int  'e mod_ring) [0..<CARD('e)]) I)"

lemma card_map_card: "card (set (map (of_int_mod_ring::int  'e mod_ring) [0..<CARD('e)])) = CARD('e)"
proof -
  have "card ((of_int_mod_ring  int::nat  'e mod_ring) ` {0..<CARD('e)}) = card {0..<CARD('e)}"
    apply (rule card_image)
    apply (rule comp_inj_on)
     apply simp
    apply (metis of_int_mod_inj_on_ff atLeast0LessThan inj_on_imageI)
    done
  then show ?thesis by force
qed

text ‹helping lemma to show that PickDistinct I prepended to I is distinct if I is distinct and 
smaller than the finite field of its elements.›
lemma PickDistinct: 
  assumes "length I < CARD('e)"
  and "distinct I"
shows "distinct ((PickDistinct I)#I)"
proof -
  have "length (filter_distinct (map of_int_mod_ring [0..<CARD('e)]) I) > 0"
    apply (rule length_filter_distinct)
    apply (insert card_map_card)
    apply (simp add: distinct_card assms) 
    done
  then have "filter_distinct (map of_int_mod_ring [0..<CARD('e)]) I  []"
    by blast
  then  have "PickDistinct I  set I"
    using set_filter_distinct[of _ I]
    by (metis (no_types, lifting) PickDistinct.elims list.set_sel(1) mem_Collect_eq)
  then show ?thesis 
    by (meson assms(2) distinct.simps(2))
qed

text ‹The reduction function takes an adversary for the hiding game and returns an 
adversary for the DL game. Specifically, the reduction uses the hiding adversary to 
construct a winning strategy for the DL game (i.e. to win it every time).
Essentially, it encodes the DL-instance in a polynomial evaluation on group elements and asks the 
hiding adversary to return the plain polynomial, which contains the solution to the DL-instance.›
fun reduction
  :: "('a vk, 'e argument, 'state) eval_hiding_adversary1  
  ('e mod_ring, 'a vk, 'a commit, 'e argument, 'e evaluation, 'a witness, 'state) 
  eval_hiding_adversary2  ('a,'e) DL.adversary"                     
where
  "reduction 𝒜1 𝒜2 g_pow_a = do {
  (α, PK)  Setup;
  (I,σ)  𝒜1 PK;
  let i = PickDistinct I;
  plain_evals  map_spmf (map ((of_int_mod_ring::int  'e mod_ring)  int)) (sample_uniform_list max_deg (order Gp));
  let evals = g_pow_a # map (λi. g ^Gpi) plain_evals ;
  let C = interpolate_on (zip (i#I) evals) α;
  let W = map (λ(x,y). (y,(C  ÷GpgGp^Gpy) ^Gp((1::'e mod_ring)/(α-x)))) (zip I plain_evals);
  φ'  𝒜2 PK σ C I W;
  return_spmf (poly φ' i)
  }"

fun reduction_tDL :: "('a vk, 'e argument, 'state) eval_hiding_adversary1 
 ('a,'e) t_DL.adversary"
  where "reduction_tDL 𝒜1 PK = do {
  (I,σ)  𝒜1 PK;
  let (α'::'e mod_ring) = (case (find (λx. g ^Gpx = PK!1) (PickDistinct I #I)) of Some x => x | None => 1);
  return_spmf α'
  }"

subsection ‹Hiding proof›

subsubsection ‹Helping lemmas›

lemma PK1[simp]: "map (λt. gGp^Gp(α^t)) [0..<max_deg+1]!1 =  gGp^Gpα"
  by (metis (no_types) Suc_eq_plus1 add.commute d_pos diff_zero landau_product_preprocess(53) 
      less_Suc_eq_0_disj nth_map_upt power_one_right)

text ‹The "find" in the tDL reduction is successful.›
lemma in_set_impl_find: "x  set xs  Some(x) = find (λy. g ^Gpy = g ^Gpx) xs"
proof - 
  assume asm: "x  set xs"
  then have "y. y  set xs  y=x"
    by blast
  then have "find (λy. y = x) xs = Some (x)"
    by (smt (verit, ccfv_threshold) find_None_iff find_Some_iff2 not_None_eq)
  then show "Some(x) = find (λy. g ^Gpy =  g ^Gpx) xs"
    by (simp add: pow_on_eq_card)
qed

lemma exchange_lem: "length I = max_deg  distinct I  α  set (PickDistinct I#I) 
    length I = max_deg  distinct I  α  set (PickDistinct I#I) 
    α = (case (find (λx. g ^Gpx = map (λt. gGp^Gp(α^t)) [0..<max_deg+1]!1) (PickDistinct I#I)) of Some x => x | None => 1)"
proof 
  assume asm: "length I = max_deg  distinct I  α  set (PickDistinct I # I)"
  then have some: "Some α =
    find (λx. g ^ x = map (λt. g ^ α ^ t) [0..<max_deg + 1] ! 1) (PickDistinct I # I)"
    apply (simp only: PK1)
    apply (rule in_set_impl_find)
    apply simp
    done
  show "length I = max_deg 
    distinct I 
    α  set (PickDistinct I # I) 
   α = (case (find (λx. g ^Gpx = map (λt. gGp^Gp(α^t)) [0..<max_deg+1]!1) (PickDistinct I#I)) of Some x => x | None => 1)"
    apply (subst some[symmetric])
    apply (insert asm)
    apply fastforce
    done
qed simp

text ‹Note, the following 3 lemmas are build up for the 4th, which states that interpolate\_on 
is equivalent to computing Commit.›

text ‹restate the interpolation of a polynomial as the sum of Lagrange basis polynomials.›
lemma eval_on_lagrange_basis: "poly (lagrange_interpolation_poly xs_ys) x  (let 
    xs = map fst xs_ys
    in sum_list (map (λ (xj,yj).  yj * (poly (lagrange_basis_poly xs xj) x)) xs_ys))"
  (is "?lhs?rhs")
proof -
  have "?lhslet 
    xs = map fst xs_ys
    in sum_list (map (λ p. poly p x) (map (λ (xj,yj). Polynomial.smult yj (lagrange_basis_poly xs xj)) xs_ys))"
    unfolding lagrange_interpolation_poly_def Let_def
    by (simp add: poly_sum_list poly_hom.hom_sum_list)
  also have "let 
    xs = map fst xs_ys
    in sum_list (map ((λ (xj,yj). poly (Polynomial.smult yj (lagrange_basis_poly xs xj)) x)) xs_ys)"
    unfolding split_def Let_def
    by (smt (verit, ccfv_SIG) length_map nth_equalityI nth_map)
  also have "let 
    xs = map fst xs_ys
    in sum_list (map ((λ (xj,yj). yj * (poly (lagrange_basis_poly xs xj) x))) xs_ys)"
    by fastforce
  finally show "?lhs  ?rhs" .
qed

text ‹This lemma is used to restate the folding operation used in the Commit function as a summing 
operation. Together with the prior lemma (i.e. interpolation is summation of Lagrange basis 
polynomials), this allows to restate Commit as the Lagrange interpolation over some points, 
evaluated at α›.›
lemma fold_on_Gp_is_sum_list: "fold (λx prod. prod Gpx) (map (λx. gGp^Gpf x) xs) (gGp^Gpz)
  = gGp^Gpz GpgGp^Gp(sum_list (map f xs))"
proof (induction xs arbitrary: z)
  case Nil
  then show ?case by simp
next
  case (Cons a xs)
  have "fold (λx prod. prod  x) (map (λx. g ^Gpf x) (a # xs)) (g ^Gpz)
    =  fold (λx prod. prod  x) (map (λx. g ^Gpf x) xs) (g ^Gp(z + f a))"
     by (simp add: mod_ring_pow_mult_Gp)
   also have " = g ^Gp(z + f a)  g ^Gpsum_list (map f xs)" 
     using Cons.IH by blast 
   also have " = g ^Gpz   g ^Gp(f a)  g ^Gpsum_list (map f xs)"
     by (simp add: mod_ring_pow_mult_Gp)
   also have " =  g ^Gpz   g ^Gpsum_list (map f (a # xs))"
     using Gp.cyclic_group_assoc mod_ring_pow_mult_Gp by force
   finally show ?case .
 qed

text ‹The two prior lemmas are pulled together to show that interpolation\_on is 
the evaluation of a polynomial on α› as a group value i.e. $g^{\phi(\alpha)}$. Note $g^{\phi(\alpha)}$ is also the
result of a correctly computed Commit.›
lemma interpolate_on_α_is_φ_of_α:
assumes dist: "distinct (map fst xs_ys)"
  and length_xs_ys: "length xs_ys  max_deg+1"
shows "interpolate_on (map (λ(x,y).(x,gGp^Gpy)) xs_ys) α 
  = g ^Gp(poly  (lagrange_interpolation_poly xs_ys) α)"
proof -
  have "interpolate_on (map (λ(x,y).(x,gGp^Gpy)) xs_ys) α = 
    (let xs = map fst (map (λ(x, y). (x, g ^Gpy)) xs_ys);
         lagrange_exp =
           map (λ(xj, y). (g ^Gpy) ^Gppoly (lagrange_basis_poly xs xj) α) xs_ys 
     in fold (λx prod. prod  x) lagrange_exp 𝟭)"
    by (smt (verit) case_prod_unfold interpolate_on_def length_map nth_equalityI nth_map prod.simps(2))
  also have " = (let xs = map fst (map (λ(x, y). (x, g ^Gpy)) xs_ys);
         lagrange_exp =
           map (λ(xj, y). g ^Gp(y * poly (lagrange_basis_poly xs xj) α)) xs_ys 
     in fold (λx prod. prod  x) lagrange_exp 𝟭)"
    using mod_ring_pow_pow_Gp by presburger
  also have " = g ^Gp((xj,
      y)xs_ys. y * poly (lagrange_basis_poly
                            (map fst (map (λ(x, y). (x, g ^Gpy)) xs_ys)) xj)
                      α)"
    using fold_on_Gp_is_sum_list[of "(λ(xj, y). (y *
               poly
                (lagrange_basis_poly (map fst (map (λ(x, y). (x, g ^Gpy)) xs_ys))
                  xj)
                α))" xs_ys 0]
    unfolding Let_def split_def by force
  also have " =  g ^Gp(poly (lagrange_interpolation_poly xs_ys) α)"
    using eval_on_lagrange_basis
    by (smt (verit, ccfv_SIG) fst_conv length_map nth_equalityI nth_map split_def)
  finally show ?thesis by fast
qed

corollary interpolate_on_α_is_φ_of_α_helper: 
  assumes "length I = max_deg  distinct I"
  and "α  set I"
shows "interpolate_on (zip (PickDistinct I # I) (map ((^Gp) g) evals)) α
  = g ^Gp(poly  (lagrange_interpolation_poly (zip (PickDistinct I # I) evals)) α)"
proof - 
  obtain xs_ys where xs_ys: "xs_ys = zip (PickDistinct I # I) evals" by fast 
  then have "length xs_ys  max_deg + 1" using assms(1) by simp
  moreover have "distinct (map fst xs_ys)" unfolding xs_ys using assms (1)
    by (metis CARD_q PickDistinct d_l_p distinct_take map_fst_zip_take of_nat_less_imp_less)
  ultimately have "interpolate_on (map (λ(x,y).(x,gGp^Gpy)) xs_ys) α 
  = g ^Gp(poly  (lagrange_interpolation_poly xs_ys) α)" using interpolate_on_α_is_φ_of_α
    by blast
  then show ?thesis unfolding xs_ys 
    by (simp add: zip_map2)
qed

text ‹Finnaly, conclude the statement that interpolate\_on is equivalent to computing Commit.
This is what the prior 3 lemmas where building up to.›
lemma interpolate_on_is_Commit:
  assumes dist: "distinct (map fst xs_ys)"
  and length_xs_ys: "length xs_ys  max_deg+1"
shows "interpolate_on (map (λ(x,y).(x,gGp^Gpy)) xs_ys) α = g_pow_PK_Prod 
    (map (λt. gGp^Gp(α^t)) [0..<max_deg + 1]) (lagrange_interpolation_poly xs_ys)"
proof -
  have "interpolate_on (map (λ(x,y).(x,gGp^Gpy)) xs_ys) α  
  =  g ^Gp(poly (lagrange_interpolation_poly xs_ys) α)"
    by(rule interpolate_on_α_is_φ_of_α[OF assms])
  also have " = g_pow_PK_Prod 
    (map (λt. g ^Gpα ^ t) [0..<max_deg + 1]) (lagrange_interpolation_poly xs_ys)"
  proof -
    have "degree (lagrange_interpolation_poly xs_ys)  max_deg"
      by (meson assms(2) degree_lagrange_interpolation_poly le_diff_conv le_trans nat_le_iff_add)
    then show "g ^Gppoly (lagrange_interpolation_poly xs_ys) α = g_pow_PK_Prod 
    (map (λt. g ^Gpα ^ t) [0..<max_deg + 1]) (lagrange_interpolation_poly xs_ys)"
    using g_pow_PK_Prod_correct by presburger
  qed
  finally show ?thesis by fast
qed

lemma split_pow_div_Gp:
  shows "g ^Gp(y/x) = (g ^Gpy) ^Gp(1/x)"
  by (metis mod_ring_pow_pow_Gp mult_cancel_left2 times_divide_eq_right)


text ‹Show that the witness emulation from the reduction adversary, is equivalent to computing 
Eval if α ∉ I›.›
lemma witness_calc_correct: 
  assumes dist: "distinct (map fst xs_ys)"
  and length_xs_ys: "length xs_ys  max_deg + 1"
  and α_nin_xs: "α  set (map fst (tl xs_ys))"
  shows "map (λi. (Eval (map (λt. g ^Gp(α ^ t)) [0..<max_deg + 1]) () (lagrange_interpolation_poly xs_ys) i)) (map fst (tl xs_ys))
    =  map (λ(x,y). (y,( gGp^Gp(poly (lagrange_interpolation_poly xs_ys) α)  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) (tl xs_ys)"
proof -
  have "?thesis = (map (λ(x,y). (Eval (map (λt. g ^Gp(α ^ t)) [0..<max_deg + 1]) () (lagrange_interpolation_poly xs_ys) x)) (tl xs_ys)
  = map (λ(x,y). (y,( gGp^Gp(poly (lagrange_interpolation_poly xs_ys) α)  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) (tl xs_ys))"
    by auto
  also have ""
  proof- 
    have "x y. (x,y)  set (tl xs_ys)  
      Eval (map (λt. g ^ α ^ t) [0..<max_deg + 1]) () (lagrange_interpolation_poly xs_ys) x
     =  (y, (g ^ poly (lagrange_interpolation_poly xs_ys) α  inv (g ^ y)) ^
                (1 / (α - x)))"
    proof -
      fix x y
      assume asm: "(x, y)  set (tl xs_ys)"
      let  = "lagrange_interpolation_poly xs_ys"

      have y: "poly (lagrange_interpolation_poly xs_ys) x = y"
          by (rule lagrange_interpolation_poly[OF assms(1)])(simp add: asm calculation in_set_tlD)+

      have deg_ψ_le: "degree (ψ_of () x)  max_deg"
        by (meson degree_lagrange_interpolation_poly degree_q_le_φ le_diff_conv le_trans
            length_xs_ys)

      have α_neg_x: "α  x"
        by (metis (mono_tags, lifting) asm assms(1,3) distinct_tl entries_keysD entries_of_alist
            keys_of_alist map_tl prod.sel(1))

      then have ψ_unfold: "poly (ψ_of  x) α = (poly  α - poly  x) / (α - x)"
        by (simp add: φx_m_φu_eq_xmu_ψx)

      show "Eval (map (λt. g ^ α ^ t) [0..<max_deg + 1]) () (lagrange_interpolation_poly xs_ys) x
     =  (y, (g ^ poly (lagrange_interpolation_poly xs_ys) α  inv (g ^ y)) ^
                (1 / (α - x)))"
      unfolding Eval_def Let_def
      apply (rule prod_eqI)
       apply (metis asm dist in_set_tlD lagrange_interpolation_poly prod.sel(1))
      apply (simp only: prod.sel(2) g_pow_PK_Prod_correct[OF deg_ψ_le])
      apply (simp only: ψ_unfold)
      apply (simp only: y)
      apply (simp only:  split_pow_div_Gp)
      apply (simp only: mod_ring_pow_mult_inv_Gp)
      done
    qed
    then show ?thesis by auto
  qed
  finally show ?thesis by fast
qed

corollary witness_calc_helper:
  assumes "length I = max_deg  distinct I"
  and "α  set I"
  and "evals
        set_spmf
           (map_spmf (map (λx. of_int_mod_ring (int x)))
             (sample_uniform_list (max_deg + 1) (order Gp)))"
shows "map (λi. (Eval (map (λt. g ^Gp(α ^ t)) [0..<max_deg + 1]) () (lagrange_interpolation_poly  (zip (PickDistinct I # I) evals)) i)) I
    =  map2 (λx y. (y,( gGp^Gp(poly (lagrange_interpolation_poly  (zip (PickDistinct I # I) evals)) α)  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) I (tl evals)"
proof - 
  obtain xs_ys where xs_ys: "xs_ys = (zip (PickDistinct I # I) evals)" by fast
  have hlength: "length evals = length (PickDistinct I#I)"
      using assms(3,1) by force
  then have h1: "map fst (tl (zip (PickDistinct I # I) evals)) = I"
      by (metis list.sel(3) map_fst_zip map_tl)
  have h2: "(tl (zip (PickDistinct I # I) evals)) = zip I (tl evals)" 
    by (metis hlength h1 map_snd_zip map_tl xs_ys zip_map_fst_snd)
  have asm1: "distinct (map fst xs_ys)" unfolding xs_ys using assms(1)
    by (metis CARD_q PickDistinct d_l_p distinct_take map_fst_zip_take of_nat_less_imp_less)
  moreover have asm2: "length xs_ys  max_deg + 1" unfolding xs_ys using assms(1) by force 
  moreover have asm3: "α  set (map fst (tl xs_ys))"
  proof (rule ccontr)
    have tl_move: "tl xs_ys = zip I (tl evals)" unfolding xs_ys
      by (metis list.sel(2,3) neq_Nil_conv zip.simps(1) zip_Cons_Cons)
    assume "¬ α  set (map fst (tl xs_ys))"
    then have "α  set (map fst (tl xs_ys))" by linarith
    then obtain y where y: "(α,y)  set (tl xs_ys)" by fastforce
    then have "α  set I" by (simp only: tl_move set_zip_leftD)
    then show "False" using assms(2) by blast
  qed
  
  ultimately have "map (λi. (Eval (map (λt. g ^Gp(α ^ t)) [0..<max_deg + 1]) () (lagrange_interpolation_poly xs_ys) i)) (map fst (tl xs_ys))
    =  map (λ(x,y). (y,( gGp^Gp(poly (lagrange_interpolation_poly xs_ys) α)  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) (tl xs_ys)"
    using witness_calc_correct by blast
  then show ?thesis unfolding xs_ys 
    using h1 h2 by argo
qed
 

text ‹show that converting a nat to a finite field element is injective on the natural numbers that 
are less than the cardinality of the finite field.›
lemma of_int_mod_inj_on_ff: "inj_on (of_int_mod_ring  int:: nat  'e mod_ring) {0..<CARD ('e)}"
proof 
  fix x 
  fix y
  assume x: "x  {0..<CARD('e)}"
  assume y: "y  {0..<CARD('e)}"
  assume app_x_eq_y: "(of_int_mod_ring  int:: nat  'e mod_ring) x = (of_int_mod_ring  int:: nat  'e mod_ring) y"
  show "x = y"
    using x y app_x_eq_y 
    by (metis atLeastLessThan_iff nat_int o_apply of_nat_0_le_iff of_nat_less_iff to_int_mod_ring_of_int_mod_ring)
qed

text ‹The proof goal is to show that the probabillity of winning the hiding game is less than or 
equal to winning the DL game. We start with the hiding game transform it via game-based methods into 
the DL game (with some reduction adversary).

We define 4 new games as subgoals in our proof:
- game1
- game2 
- game2\_w\_asert
- game2\_wo\_assert

The proof is structered into 5 steps, each step targets one subgoal, except the fifth one, where the 
trarget is DL game: 

1. Transform the hiding game into game 1. We exchange Commit with interpolate\_on and prepare the 
game to easily replace CreateWitness with the witness emulation of the reduction adversary. We use 
only game hops based on bridging steps in this step.
  
2. We use a game-hop based on a failure event to transform game1 into game2. We extract the 
negligible probabillity that α› is contained in I, as in that case, CreateWitness is not the same as 
the emulation in the reduction adversary. 

3. We use game-hops based on bridging steps to explicitly restate game2 as game2\_w\_assert. 
We extract the assert that the adversary guessed polynomial φ'› equals φ›, which is part of the hiding
game but not of the DL reduction.

4. We use over-estimation to drop the φ' = φ›-assert. We obtain game2\_wo\_assert from game2\_w\_assert.

5. We transform game2\_wo\_assert into the DL game using game hops based on bridging steps.

For a more intuitive understanding of the steps go to the final theorem at the end of the file.
›

text ‹The hiding game with interpolate\_on instead of Commit and the random sampling of φ› split up
into sampling random points for interpolate\_on.›
definition game1 :: "('a vk, 'e argument, 'state) eval_hiding_adversary1  
  ('e mod_ring, 'a vk, 'a commit, 'e argument, 'e evaluation, 'a witness, 'state) 
  eval_hiding_adversary2  bool spmf" where 
  "game1 𝒜1 𝒜2 = TRY do {
  (α, PK)  Setup;
  (I,σ)  𝒜1 PK;
  _ :: unit  assert_spmf (length I = max_deg  distinct I);
  let i = PickDistinct I;
  evals  map_spmf (map  (of_int_mod_ring  int::nat  'e mod_ring)) (sample_uniform_list (max_deg+1) (order Gp));
  let φ = lagrange_interpolation_poly (zip (i#I) evals);
  let exp_evals = map (λi. gGp^Gpi) evals;
  let C = interpolate_on (zip (i#I) exp_evals) α;
  let W = map (λj. Eval PK () φ j) I;
  φ'  𝒜2 PK σ C I W;                             
  return_spmf (φ = φ')} ELSE return_spmf False"

text ‹game1 with the reduction adversaries emulation instead of CreateWitness›
definition game2 :: "('a vk, 'e argument, 'state) eval_hiding_adversary1  
  ('e mod_ring, 'a vk, 'a commit, 'e argument, 'e evaluation, 'a witness, 'state) 
  eval_hiding_adversary2  bool spmf" where 
  "game2 𝒜1 𝒜2 = TRY do {
  (α, PK)  Setup;
  (I,σ)  𝒜1 PK;
  _ :: unit  assert_spmf (length I = max_deg  distinct I);
  let i = PickDistinct I;
  evals  map_spmf (map  (of_int_mod_ring  int)) (sample_uniform_list (max_deg+1) (order Gp));
  let φ = lagrange_interpolation_poly (zip (i#I) evals);
  let exp_evals = map (λi. g ^ i) evals;
  let C = interpolate_on (zip (i#I) exp_evals) α;
  let witn_tupel = map (λ(x,y). (y,(C  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) (zip I (tl evals));
  φ'  𝒜2 PK σ C I witn_tupel;
  return_spmf (φ = φ')} ELSE return_spmf False"

text ‹game 2 with extracted φ = φ'›-assert›
definition game2_w_assert :: "('a vk, 'e argument, 'state) eval_hiding_adversary1  
  ('e mod_ring, 'a vk, 'a commit, 'e argument, 'e evaluation, 'a witness, 'state) 
  eval_hiding_adversary2  bool spmf" where 
  "game2_w_assert 𝒜1 𝒜2 = TRY do {
  (α, PK)  Setup;
  (I,σ)  𝒜1 PK;
  _ :: unit  assert_spmf (length I = max_deg  distinct I);
  let i = PickDistinct I;
  evals  map_spmf (map  (of_int_mod_ring  int)) (sample_uniform_list (max_deg+1) (order Gp));
  let φ = lagrange_interpolation_poly (zip (i#I) evals);
  let C = interpolate_on (zip (i#I) (map (λi. g ^ i) evals)) α;
  let witn_tupel = map (λ(x,y). (y,(C  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) (zip I (tl evals));
  φ'  𝒜2 PK σ C I witn_tupel;
  _::unit  assert_spmf (φ = φ');
  return_spmf (hd evals = poly φ' i)} ELSE return_spmf False"

text ‹game2\_w\_asssert without the assert.›
definition game2_wo_assert :: "('a vk, 'e argument, 'state) eval_hiding_adversary1  
  ('e mod_ring, 'a vk, 'a commit, 'e argument, 'e evaluation, 'a witness, 'state) 
  eval_hiding_adversary2  bool spmf" where 
  "game2_wo_assert 𝒜1 𝒜2 = TRY do {
  (α, PK)  Setup;
  (I,σ)  𝒜1 PK; 
  let i = PickDistinct I;
  evals  map_spmf (map  (of_int_mod_ring  int)) (sample_uniform_list (max_deg+1) (order Gp));
  let φ = lagrange_interpolation_poly (zip (i#I) evals);
  (α, PK)  Setup;
  let C = interpolate_on (zip (i#I) (map (λi. g ^ i) evals)) α;
  let witn_tupel = map (λ(x,y). (y,(C  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) (zip I (tl evals));
  φ'  𝒜2 PK σ C I witn_tupel;
  return_spmf (hd evals = poly φ' i)} ELSE return_spmf False"

lemma PickDistinct_Prop: "length a = max_deg  distinct a
   distinct (PickDistinct a#a)  length (PickDistinct a#a) = max_deg + 1"
  by (metis CARD_q PickDistinct Suc_eq_plus1 d_l_p length_Cons
      of_nat_less_iff)

corollary PickDistinct_Prop' : "length a = max_deg  distinct a  valid_poly φ
   distinct (PickDistinct a#a)  length (PickDistinct a#a) = max_deg + 1"
  using PickDistinct_Prop by presburger

lemma distinct_map_fst_zip: "distinct a  distinct (map fst (zip a b))"
  by (simp add: map_fst_zip_take)

lemma card_eq_ord: "CARD('e) = order Gp"
  using CARD_q CARD_Gp by force

text ‹Step 1 of the proof.›
lemma hiding_game_to_game1:
shows "spmf (weak_eval_hiding_game 𝒜1 𝒜2) True = spmf (game1 𝒜1 𝒜2) True"
 (is "?lhs = ?rhs")
proof -
  text ‹We unfold the weak eval hiding game›
  have "?lhs = spmf (TRY do {
  p::'e mod_ring poly  sample_uniform_poly max_deg;
  (ck, vk)  key_gen;
  (I,σ)  do {
    (I,σ)  𝒜1 vk;
    _ :: unit  assert_spmf (length I = max_deg  distinct I);
    return_spmf (I,σ)
    };
  (c,d)  commit ck p; 
  let W = map (λi. Eval ck d p i) I; 
  p'  𝒜2 vk σ c I W;
  return_spmf (p = p')} ELSE return_spmf False) True"
    unfolding weak_eval_hiding_game_def eval_hiding_game_def weak_eval_hiding_adversary1_def
    by (fold try_bind_spmf_lossless2[OF lossless_return_spmf])(simp)
  also have " = spmf (TRY do {
  p::'e mod_ring poly  sample_uniform_poly max_deg;
  (ck, vk)  key_gen;
  (I,σ)  𝒜1 vk;
  _ :: unit  assert_spmf (length I = max_deg  distinct I);
  (c,d)  commit ck p; 
  let W = map (λi. Eval ck d p i) I; 
  p'  𝒜2 vk σ c I W;
  return_spmf (p = p')} ELSE return_spmf False) True"
    by (simp add: key_gen_def bind_map_spmf o_def Setup_def split_def Let_def 
       del: PickDistinct.simps)
  text ‹sample phi uniform random is sampling evaluation points for the adversaries I uniform random›
  also have " = spmf (TRY do {
  (ck, vk)  key_gen;
  (I,σ)  𝒜1 vk;
  p::'e mod_ring poly  do {
    _ :: unit  assert_spmf (length I = max_deg  distinct I);
    evals::'e mod_ring list  map_spmf (map (of_int_mod_ring  int:: nat  'e mod_ring)) (sample_uniform_list (max_deg+1) (CARD ('e)));
    let i = PickDistinct I;
    return_spmf (lagrange_interpolation_poly (zip (i#I) evals))
    };
  (c,d)  commit ck p; 
  let W = map (λi. Eval ck d p i) I; 
  p'  𝒜2 vk σ c I W;
  return_spmf (p = p')} ELSE return_spmf False) True"
    apply (rule spmf_eqI')
    apply (rule unpack_try_spmf)
    apply (subst bind_commute_spmf; rule unpack_bind_spmf';rule ext; 
        simp 
        split: prod.splits 
        add: split_paired_all 
        del: return_bind_spmf bind_spmf_assoc PickDistinct.simps)+
    apply (subst bind_commute_spmf)
    apply (subst bind_spmf_assoc)
    apply (rule assert_based_eq)
    apply (drule PickDistinct_Prop)
    subgoal for ck vk I σ
      apply (subst sample_uniform_evals_is_sample_poly[of "(PickDistinct I # I)" max_deg])
        apply simp+
      done
    done
  text ‹Now we bring the game in a nicer form and expose the Setup from key\_gen›
  also have " = spmf(TRY do {
  α::'e mod_ring   map_spmf (λx. of_int_mod_ring (int x)) (sample_uniform (order Gp));
  let PK = map (λt. gGp^Gp(α^t)) [0..<max_deg+1];
  (I,σ)  𝒜1 PK;
  _ :: unit  assert_spmf (length I = max_deg  distinct I);
  evals::'e mod_ring list  map_spmf (map (of_int_mod_ring  int:: nat  'e mod_ring)) (sample_uniform_list (max_deg+1) (CARD ('e)));
  let i = PickDistinct I;
  let p = (lagrange_interpolation_poly (zip (i#I) evals));
  (c,d)  commit PK p; 
  let W = map (λi. Eval PK d p i) I; 
  p'  𝒜2 PK σ c I W;
  return_spmf (p = p')} ELSE return_spmf False) True"
   by (simp add: key_gen_def bind_map_spmf o_def Setup_def split_def Let_def 
       del: PickDistinct.simps)
  text ‹Now we replace the Commit computed with a valid public key PK by interpolate\_on›
  also have " = spmf(TRY do {
  α::'e mod_ring   map_spmf (λx. of_int_mod_ring (int x)) (sample_uniform (order Gp));
  let PK = map (λt. gGp^Gp(α^t)) [0..<max_deg+1];
  (I,σ)  𝒜1 PK;
  _ :: unit  assert_spmf (length I = max_deg  distinct I);
  evals::'e mod_ring list  map_spmf (map (of_int_mod_ring  int:: nat  'e mod_ring)) (sample_uniform_list (max_deg+1) (CARD ('e)));
  let i = PickDistinct I;
  let p = (lagrange_interpolation_poly (zip (i#I) evals));
  let exp_evals = map (λi. gGp^Gpi) evals;
  let C = interpolate_on (zip (i#I) exp_evals) α;
  let W = map (λi. Eval PK () p i) I; 
  p'  𝒜2 PK σ C I W;
  return_spmf (p = p')} ELSE return_spmf False) True"
    apply (rule spmf_eqI')
    apply (simp only: Let_def split_def)
    apply (rule unpack_try_spmf; rule unpack_bind_spmf')+
    apply (rule ext; simp split: prod.splits add: split_paired_all 
        del: return_bind_spmf bind_spmf_assoc PickDistinct.simps One_nat_def; 
        rule unpack_bind_spmf'; rule ext)
    apply (rule assert_based_eq)
    apply (simp split: prod.splits add: split_paired_all 
        del: return_bind_spmf bind_spmf_assoc PickDistinct.simps One_nat_def)
    apply (drule PickDistinct_Prop)
    apply (simp only: commit_def return_bind_spmf bind_return_spmf)
    apply (subst interpolate_on_is_Commit[symmetric, of ])
      apply (simp add: map_fst_zip_take)
     apply simp
    apply (simp add: zip_map2)
    done
  also have " = ?rhs" 
    by (simp add: game1_def Setup_def Let_def split_def card_eq_ord bind_map_spmf o_def 
        del: PickDistinct.simps)
  finally show ?thesis .
qed

lemma lossless_mod_ring_list: "lossless_spmf (map_spmf (map (λx. of_int_mod_ring (int x)::'e mod_ring))
            (sample_uniform_list (max_deg + 1) (order Gp)))"
  by (simp add: Gp.order_gt_0)

text ‹Step 2 of the proof:›
lemma fundamental_lemma_game1_game2: 
  assumes lossless_𝒜1: "PK . lossless_spmf (𝒜1 PK)"
  and lossless_𝒜2: "PK σ C I W . lossless_spmf (𝒜2 PK σ C I W)"
  shows "spmf (game1 𝒜1 𝒜2) True  spmf (game2 𝒜1 𝒜2) True + t_DL_Gp.advantage (reduction_tDL 𝒜1)"
proof -
  note [simp] = map_lift_spmf gpv.map_id lossless_weight_spmfD map_spmf_bind_spmf bind_spmf_const Let_def o_def
  note %invisible [cong del] = if_weak_cong 
   and [split del] = if_split
   and [simp] = map_lift_spmf gpv.map_id lossless_weight_spmfD map_spmf_bind_spmf bind_spmf_const
   and [if_distribs] = if_distrib[where f="λx. try_spmf x _"] if_distrib[where f="weight_spmf"] if_distrib[where f="λr. scale_spmf r _"]

  define game1b :: "('a vk, 'e argument, 'f) eval_hiding_adversary1  
  ('e mod_ring, 'a vk, 'a commit, 'e argument, 'e evaluation, 'a witness, 'f)
  eval_hiding_adversary2  (bool×bool) spmf"
    where "game1b 𝒜1 𝒜2 = TRY do {
  α::'e mod_ring  map_spmf (of_int_mod_ring  int) (sample_uniform (order Gp));
  let PK =  map (λt. gGp^Gp(α^t)) [0..<max_deg+1];
  (I,σ)  𝒜1 PK;
  _ :: unit  assert_spmf (length I = max_deg  distinct I);
  let i = PickDistinct I;
  evals  map_spmf (map  (of_int_mod_ring  int::nat  'e mod_ring)) (sample_uniform_list (max_deg+1) (order Gp));
  let φ = lagrange_interpolation_poly (zip (i#I) evals);
  let exp_evals = map (λi. gGp^Gpi) evals;
  let C = interpolate_on (zip (i#I) exp_evals) α;
  let W = map (λj. Eval PK () φ j) I;
  φ'  𝒜2 PK σ C I W;
  return_spmf (φ = φ', α  set (PickDistinct I#I))} ELSE return_spmf (False,False)"
    for 𝒜1 𝒜2

  text ‹show game1 is game1b with random sampling, filtered for the first result i.e. neglecting 
  whether the failure event occured›
  have game1b: "game1 𝒜1 𝒜2 = map_spmf fst (game1b 𝒜1 𝒜2)"
  proof -
    have "game1 𝒜1 𝒜2 = TRY do {
      α :: 'e mod_ring  map_spmf (of_int_mod_ring  int) (sample_uniform (order Gp));
      let PK =  map (λt. gGp^Gp(α^t)) [0..<max_deg+1];
      (I,σ)  𝒜1 PK;
      _ :: unit  assert_spmf (length I = max_deg  distinct I);
      let i = PickDistinct I;
      evals  map_spmf (map  (of_int_mod_ring  int::nat  'e mod_ring)) (sample_uniform_list (max_deg+1) (order Gp));
      let φ = lagrange_interpolation_poly (zip (i#I) evals);
      let exp_evals = map (λi. gGp^Gpi) evals;
      let C = interpolate_on (zip (i#I) exp_evals) α;
      let W = map (λj. Eval PK () φ j) I;
      φ'  𝒜2 PK σ C I W;
    return_spmf (φ = φ')} ELSE return_spmf False"
      by (simp add: bind_map_spmf game1_def Setup_def)
    also have " = map_spmf fst (game1b 𝒜1 𝒜2)"
      unfolding game1b_def
      apply (simp add: map_try_spmf try_spmf_bind_spmf_lossless lossless_𝒜1 
          del: bind_spmf_const bind_spmf_assoc PickDistinct.simps One_nat_def)
      apply (rule unpack_try_spmf)
      apply (rule unpack_bind_spmf';rule ext)+
      apply (simp split: prod.splits add: split_paired_all 
          del: bind_spmf_const bind_spmf_assoc PickDistinct.simps One_nat_def)
      done
    finally show ?thesis .
  qed

   define game2b :: "('a vk, 'e argument, 'f) eval_hiding_adversary1  
  ('e mod_ring, 'a vk, 'a commit, 'e argument, 'e evaluation, 'a witness, 'f)
  eval_hiding_adversary2  (bool×bool) spmf"
    where "game2b 𝒜1 𝒜2 = TRY do {
  α::'e mod_ring  map_spmf (of_int_mod_ring  int) (sample_uniform (order Gp));
  let PK =  map (λt. gGp^Gp(α^t)) [0..<max_deg+1];
  (I,σ)  𝒜1 PK;
  _ :: unit  assert_spmf (length I = max_deg  distinct I);
  let i = PickDistinct I;
  evals  map_spmf (map  (of_int_mod_ring  int)) (sample_uniform_list (max_deg+1) (order Gp));
  let φ = lagrange_interpolation_poly (zip (i#I) evals);
  let exp_evals = map (λi. g ^ i) evals;
  let C = interpolate_on (zip (i#I) exp_evals) α;
  let witn_tupel = map (λ(x,y). (y,(C  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) (zip I (tl evals));
  φ'  𝒜2 PK σ C I witn_tupel;
  return_spmf (φ = φ', α  set (PickDistinct I#I))} ELSE return_spmf (False, False)" 
    for 𝒜1 𝒜2

  text ‹accordingly we show game2 is game2b with random sampling, filtered for the first result 
  i.e. neglecting whether the failure event occured›
  have game2b: "game2 𝒜1 𝒜2 = map_spmf fst (game2b 𝒜1 𝒜2)"
  proof -
    have "game2 𝒜1 𝒜2 = TRY do {
    α :: 'e mod_ring  map_spmf (of_int_mod_ring  int) (sample_uniform (order Gp));
    let PK =  map (λt. gGp^Gp(α^t)) [0..<max_deg+1];
    (I,σ)  𝒜1 PK;
      _ :: unit  assert_spmf (length I = max_deg  distinct I);
    let i = PickDistinct I;
    evals  map_spmf (map  (of_int_mod_ring  int)) (sample_uniform_list (max_deg+1) (order Gp));
    let φ = lagrange_interpolation_poly (zip (i#I) evals);
    let exp_evals = map (λi. g ^ i) evals;
    let C = interpolate_on (zip (i#I) exp_evals) α;
    let witn_tupel = map (λ(x,y). (y,(C  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) (zip I (tl evals));
    φ'  𝒜2 PK σ C I witn_tupel;
    return_spmf (φ = φ')} ELSE return_spmf False"
      by (simp add: bind_map_spmf game2_def Setup_def)
    also have " = map_spmf fst (game2b 𝒜1 𝒜2)"
      unfolding game2b_def
      apply (simp add: map_try_spmf try_spmf_bind_spmf_lossless lossless_𝒜1 
          del: bind_spmf_const bind_spmf_assoc PickDistinct.simps One_nat_def)
      apply (rule unpack_try_spmf)
      apply (rule unpack_bind_spmf';rule ext)+
      apply (simp split: prod.splits add: split_paired_all 
          del: bind_spmf_const bind_spmf_assoc PickDistinct.simps One_nat_def)
      done
    finally show ?thesis .
  qed

  text ‹We capture the failure event (i.e. α ∈ I›) in the collision game.›
  define collision_game :: "('a vk, 'e argument, 'f) eval_hiding_adversary1  bool spmf" where 
  "collision_game 𝒜1 = TRY do {
     α::'e mod_ring  map_spmf (of_int_mod_ring  int) (sample_uniform (order Gp));
     let PK =  map (λt. gGp^Gp(α^t)) [0..<max_deg+1];
     (I,σ)  𝒜1 PK;
     _ :: unit  assert_spmf (length I = max_deg  distinct I); 
     return_spmf (α  set (PickDistinct I#I))} ELSE return_spmf False" for 𝒜1

  text ‹We show that the second result of game1b captures the failure event, i.e. is the collision 
  game.›
  have map_snd_game1_is_collision_game: "map_spmf snd (game1b 𝒜1 𝒜2) = collision_game 𝒜1"
    apply (simp only: game1b_def collision_game_def)
    apply (simp add: map_try_spmf try_spmf_bind_spmf_lossless lossless_𝒜1
          del: bind_spmf_const bind_spmf_assoc PickDistinct.simps One_nat_def)
    apply (rule unpack_try_spmf)
    apply (rule unpack_bind_spmf';rule ext)+
    apply (simp split: prod.splits add: split_paired_all 
          del: bind_spmf_const bind_spmf_assoc PickDistinct.simps One_nat_def) (* return_bind_spmf*)
    apply (rule unpack_bind_spmf';rule ext) 
    apply (simp add: map_try_spmf del: PickDistinct.simps One_nat_def bind_spmf_assoc bind_spmf_const)
    apply (simp only: try_spmf_bind_spmf_lossless try_spmf_lossless 
        lossless_mod_ring_list lossless_𝒜2 lossless_return_spmf 
        bind_spmf_independent_return_spmf)
    done

   text ‹We show that the second result of game1b captures the failure event, i.e. is the collision 
  game.›
  have map_snd_game1_is_collision_game': "map_spmf snd (game1b 𝒜1 𝒜2) = collision_game 𝒜1"
    apply (simp only: game1b_def collision_game_def)
    apply (simp add: map_try_spmf try_spmf_bind_spmf_lossless lossless_𝒜1
          del: bind_spmf_const bind_spmf_assoc PickDistinct.simps One_nat_def)
    apply (rule unpack_try_spmf)
    apply (rule unpack_bind_spmf';rule ext)+
    apply (simp split: prod.splits add: split_paired_all 
          del: bind_spmf_const bind_spmf_assoc PickDistinct.simps One_nat_def)
    apply (rule unpack_bind_spmf';rule ext) 
    apply (simp add: map_try_spmf del: PickDistinct.simps One_nat_def bind_spmf_assoc bind_spmf_const)
    apply (simp only: try_spmf_bind_spmf_lossless try_spmf_lossless 
        lossless_mod_ring_list lossless_𝒜2 lossless_return_spmf 
        bind_spmf_independent_return_spmf)
    done

   text ‹Before we start performing the game-hop with the fundamental lemma, we state the 
  probability of winning the failure event. That is less than or equal to breaking the 
  t-DL assumption›
  have spmf_collision_game: "spmf (collision_game 𝒜1) True
   t_DL_Gp.advantage (reduction_tDL 𝒜1)"
  proof -
    have "spmf (collision_game 𝒜1) True = spmf (TRY do {
     x  sample_uniform (order Gp);
     let (α::'e mod_ring) = (of_int_mod_ring  int) x;
     let PK =  map (λt. gGp^Gp(α^t)) [0..<max_deg+1];
     (I,σ)  𝒜1 PK;
     _ :: unit  assert_spmf (length I = max_deg  distinct I); 
     return_spmf (α  set (PickDistinct I#I))} ELSE return_spmf False) True"
      by (simp add: collision_game_def bind_map_spmf del: PickDistinct.simps)
     also have " = spmf (TRY do {
     x  sample_uniform (order Gp);
     TRY do {
     let (α::'e mod_ring) = (of_int_mod_ring  int) x;
     let PK =  map (λt. gGp^Gp(α^t)) [0..<max_deg+1];
     (I,σ)  𝒜1 PK;
      TRY do {
     _ :: unit  assert_spmf (length I = max_deg  distinct I); 
      TRY do {
     _ :: unit  assert_spmf (α  set (PickDistinct I#I));
     return_spmf True} ELSE return_spmf False
    } ELSE return_spmf False
    } ELSE return_spmf False
    } ELSE return_spmf False) True"
       unfolding Let_def split_def
       apply (simp only: return_spmf_assert[symmetric])
       apply (fold try_bind_spmf_lossless2[OF lossless_return_spmf]) 
       apply simp
       done
     also have " = spmf (TRY do {
     x  sample_uniform (order Gp);
     let (α::'e mod_ring) = (of_int_mod_ring  int) x;
     let PK =  map (λt. gGp^Gp(α^t)) [0..<max_deg+1];
     (I,σ)  𝒜1 PK;
     _ :: unit  assert_spmf (length I = max_deg  distinct I); 
     _ :: unit  assert_spmf (α  set (PickDistinct I#I));
     return_spmf True} ELSE return_spmf False) True"
       unfolding Let_def split_def
        by (fold try_bind_spmf_lossless2[OF lossless_return_spmf])simp
     also have " = spmf (TRY do {
     x  sample_uniform (order Gp);
     let (α::'e mod_ring) = (of_int_mod_ring  int) x;
     let PK =  map (λt. gGp^Gp(α^t)) [0..<max_deg+1];
     (I,σ)  𝒜1 PK;
     _ :: unit  assert_spmf (length I = max_deg  distinct I  α  set (PickDistinct I#I)); 
     return_spmf True} ELSE return_spmf False) True"
       by (simp add: assert_collapse 
           del: bind_spmf_const bind_spmf_assoc PickDistinct.simps One_nat_def)
      also have " = spmf (TRY do {
     x  sample_uniform (order Gp);
     let (α::'e mod_ring) = (of_int_mod_ring  int) x;
     let PK =  map (λt. gGp^Gp(α^t)) [0..<max_deg+1];
     (I,σ)  𝒜1 PK;
     let (α'::'e mod_ring) = (case (find (λx. g ^Gpx = PK!1) (PickDistinct I#I)) of Some x => x | None => 1);
     _ :: unit  assert_spmf (length I = max_deg  distinct I  α  set (PickDistinct I#I)); 
     _ :: unit  assert_spmf (α = (case (find (λx. g ^Gpx = map (λt. gGp^Gp(α^t)) [0..<max_deg+1]!1) (PickDistinct I#I)) of Some x => x | None => 1)); 
     return_spmf True} ELSE return_spmf False) True"
        apply (simp only: Let_def)
        apply (subst exchange_lem)
        apply (simp add: assert_collapse 
            del: bind_spmf_const PickDistinct.simps One_nat_def)
        done
       also have "  spmf (TRY do {
     α  sample_uniform (order Gp);
     let (α::'e mod_ring) = (of_int_mod_ring  int) α;
     let PK =  map (λt. gGp^Gp(α^t)) [0..<max_deg+1];
     (I,σ)  𝒜1 PK;
     let α' = (case (find (λx. g ^Gpx = PK!1) (PickDistinct I#I)) of Some x => x | None => 1);
      _ :: unit  assert_spmf (α = (case (find (λx. g ^Gpx = map (λt. gGp^Gp(α^t)) [0..<max_deg+1]!1) (PickDistinct I#I)) of Some x => x | None => 1));
     return_spmf True
      } ELSE return_spmf False) True"
         apply (simp only: Let_def split_def)
         apply (rule try_spmf_le)
         apply (rule bind_spmf_le)+
         apply (simp add: del_assert del: bind_spmf_const PickDistinct.simps One_nat_def)
         done
       also have " = spmf (TRY do {
     α  sample_uniform (order Gp);
     TRY do {
     let (α::'e mod_ring) = (of_int_mod_ring  int) α;
     let PK =  map (λt. gGp^Gp(α^t)) [0..<max_deg+1];
     (I,σ)  𝒜1 PK;
     TRY do {
     let α' = (case (find (λx. g ^Gpx = PK!1) (PickDistinct I#I)) of Some x => x | None => 1);
     return_spmf (α = (case (find (λx. g ^Gpx = map (λt. gGp^Gp(α^t)) [0..<max_deg+1]!1) (PickDistinct I#I)) of Some x => x | None => 1))
      } ELSE return_spmf False
      } ELSE return_spmf False
      } ELSE return_spmf False) True"
       unfolding Let_def split_def
       apply (simp only: return_spmf_assert)
       apply (fold try_bind_spmf_lossless2[OF lossless_return_spmf]) 
       apply (simp del: PickDistinct.simps One_nat_def)
       done
      also have " = spmf (TRY do {
     α  sample_uniform (order Gp);
     let (α::'e mod_ring) = (of_int_mod_ring  int) α;
     let PK =  map (λt. gGp^Gp(α^t)) [0..<max_deg+1];
     (I,σ)  𝒜1 PK;
     let α' = (case (find (λx. g ^Gpx = PK!1) (PickDistinct I#I)) of Some x => x | None => 1);
     return_spmf (α = (case (find (λx. g ^Gpx = map (λt. gGp^Gp(α^t)) [0..<max_deg+1]!1) (PickDistinct I#I)) of Some x => x | None => 1))
      } ELSE return_spmf False) True"
        by (fold try_bind_spmf_lossless2[OF lossless_return_spmf])simp
      also have " = spmf (TRY do {
     α  sample_uniform (order Gp);
     let (α::'e mod_ring) = (of_int_mod_ring  int) α;
     let PK =  map (λt. gGp^Gp(α^t)) [0..<max_deg+1];
     α'  do {
        (I,σ)  𝒜1 PK;
        let (α'::'e mod_ring) = (case (find (λx. g ^Gpx = PK!1) (PickDistinct I#I)) of Some x => x | None => 1);
        return_spmf α'};
     return_spmf (α = α')
      } ELSE return_spmf False) True"
        unfolding Let_def split_def by force
      also have " = t_DL_Gp.advantage (reduction_tDL 𝒜1)"
        unfolding t_DL_Gp.advantage_def t_DL_Gp.game_def reduction_tDL.simps Let_def split_def
        by (simp del: PickDistinct.simps One_nat_def Let_def)
      finally show ?thesis .
    qed
  
  text ‹We now use relation reasoning to state that game1 and game2 are equal except for the 
  failure event.›
  have "rel_spmf (λ(win, bad) (win', bad'). (bad  bad')  (¬ bad'  win  win')) (game1b 𝒜1 𝒜2) (game2b 𝒜1 𝒜2)"
    apply (simp only: game1b_def game2b_def)
    apply (intro rel_spmf_try_spmf)
     apply (simp del: bind_spmf_const bind_spmf_assoc PickDistinct.simps One_nat_def)
     apply (rule rel_spmf_bind_reflI)
     apply (rule rel_spmf_bind_reflI)
     apply (simp split: prod.splits add: split_paired_all 
        del: return_bind_spmf bind_spmf_const bind_spmf_assoc PickDistinct.simps One_nat_def)
     apply (rule rel_spmf_bind_assert_reflI)
     apply (rule rel_spmf_bind_reflI)
    subgoal for α I σ evals
      apply (cases "α  set I")
       apply (simp add: rel_spmf_bindI1 rel_spmf_bindI2 lossless_𝒜2
          del: PickDistinct.simps One_nat_def bind_spmf_assoc bind_spmf_const)
      apply (rule rel_spmf_bindI[of "(=)"])
       apply (simp only: spmf_rel_eq)
       apply (simp add: witness_calc_helper interpolate_on_α_is_φ_of_α_helper
          del: PickDistinct.simps One_nat_def bind_spmf_assoc bind_spmf_const)
      apply (simp del: PickDistinct.simps One_nat_def bind_spmf_assoc bind_spmf_const)+
      done
    apply (simp del: PickDistinct.simps One_nat_def bind_spmf_assoc bind_spmf_const)+
    done
  text ‹We use the fundamental lemma to conclude that the difference in winnig one or the other game 
  differs at most in the probability of the failure event.›
  hence "¦measure (measure_spmf (game1b 𝒜1 𝒜2)) {(win, _). win} - measure (measure_spmf (game2b 𝒜1 𝒜2)) {(win, _). win}¦
       measure (measure_spmf (game1b 𝒜1 𝒜2)) {(_, bad). bad}"
  unfolding split_def by(rule fundamental_lemma)
  moreover have "measure (measure_spmf (game1b 𝒜1 𝒜2)) {(win, _). win} = spmf (map_spmf fst (game1b 𝒜1 𝒜2)) True"
    and "measure (measure_spmf (game2b 𝒜1 𝒜2)) {(win, _). win} = spmf (map_spmf fst (game2b 𝒜1 𝒜2)) True"
    and "measure (measure_spmf (game1b 𝒜1 𝒜2)) {(_, bad). bad} = spmf (map_spmf snd (game1b 𝒜1 𝒜2)) True"
      unfolding spmf_conv_measure_spmf measure_map_spmf by(auto simp add: vimage_def split_def)
 text ‹We use this to overestimate the probability of game1 by the probability of winning game2 
 plus the probability of the failure event happening›
 ultimately have
    "¦spmf (map_spmf fst (game1b 𝒜1 𝒜2)) True - spmf (map_spmf fst (game2b 𝒜1 𝒜2)) True¦
     spmf (map_spmf snd (game1b 𝒜1 𝒜2)) True"
    by simp
  then have "spmf (map_spmf fst (game1b 𝒜1 𝒜2)) True  spmf (map_spmf fst (game2b 𝒜1 𝒜2)) True 
    + spmf (map_spmf snd (game1b 𝒜1 𝒜2)) True"
    by linarith
  then show ?thesis
    unfolding game1b game2b map_snd_game1_is_collision_game 
    using spmf_collision_game
    by force
qed


text ‹Step 3 of the proof:›
lemma game2_le_DL: "spmf (game2 𝒜1 𝒜2) True  DL_Gp.advantage (reduction 𝒜1 𝒜2)"
  (is "?lhs  ?rhs")
proof -
  have "?lhs = spmf (TRY do {
  (α, PK)  Setup;
  TRY do{
  (I,σ)  𝒜1 PK;
  TRY do{
  _ :: unit  assert_spmf (length I = max_deg  distinct I);
  let i = PickDistinct I;
  TRY do{
  evals  map_spmf (map  (of_int_mod_ring  int)) (sample_uniform_list (max_deg+1) (order Gp));
  let φ = lagrange_interpolation_poly (zip (i#I) evals);
  let exp_evals = map (λi. g ^ i) evals;
  let C = interpolate_on (zip (i#I) exp_evals) α;
  let witn_tupel = map (λ(x,y). (y,(C  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) (zip I (tl evals));
  TRY do{
  φ'  𝒜2 PK σ C I witn_tupel;
  TRY do{
  _ :: unit  assert_spmf (φ = φ');
  return_spmf True
  } ELSE return_spmf False
  } ELSE return_spmf False
  } ELSE return_spmf False
  } ELSE return_spmf False
  } ELSE return_spmf False
  } ELSE return_spmf False) True"
    unfolding game2_def split_def Let_def split_def
    apply (simp only: return_spmf_assert[symmetric])
    apply (fold try_bind_spmf_lossless2[OF lossless_return_spmf])
    apply (simp del: PickDistinct.simps) 
    done
  also have " = spmf (TRY do {
  (α, PK)  Setup;
  (I,σ)  𝒜1 PK;
  _ :: unit  assert_spmf (length I = max_deg  distinct I);
  let i = PickDistinct I;
  evals  map_spmf (map  (of_int_mod_ring  int)) (sample_uniform_list (max_deg+1) (order Gp));
  let φ = lagrange_interpolation_poly (zip (i#I) evals);
  let exp_evals = map (λi. g ^ i) evals;
  let C = interpolate_on (zip (i#I) exp_evals) α;
  let witn_tupel = map (λ(x,y). (y,(C  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) (zip I (tl evals));
  φ'  𝒜2 PK σ C I witn_tupel;
  _ :: unit  assert_spmf (φ = φ');
  return_spmf True
  } ELSE return_spmf False) True"
    unfolding Let_def split_def 
    by (fold try_bind_spmf_lossless2[OF lossless_return_spmf])
       (simp del: PickDistinct.simps One_nat_def) 
   text ‹next we show that the assert φ = φ'› implies hd evals = poly φ' (PickDistinct I)›, which is
 asserted by the Dl reduction game. Hence we add hd evals = poly φ' (PickDistinct I)› to the
 assert without changing the game.›
   also have " = spmf (TRY do {
  (α, PK)  Setup;
  (I,σ)  𝒜1 PK;
  _ :: unit  assert_spmf (length I = max_deg  distinct I);
  let i = PickDistinct I;
  evals  map_spmf (map  (of_int_mod_ring  int)) (sample_uniform_list (max_deg+1) (order Gp));
  let φ = lagrange_interpolation_poly (zip (i#I) evals);
  let exp_evals = map (λi. g ^ i) evals;
  let C = interpolate_on (zip (i#I) exp_evals) α;
  let witn_tupel = map (λ(x,y). (y,(C  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) (zip I (tl evals));
  φ'  𝒜2 PK σ C I witn_tupel;
  _ :: unit  assert_spmf (φ = φ'  hd evals = poly φ' i);
  return_spmf True
  } ELSE return_spmf False) True"
  proof -
    have equi: "I evals φ'. length I = max_deg  distinct I 
       evals  set_spmf (map_spmf (map  (of_int_mod_ring  int)) 
          (sample_uniform_list (max_deg+1) (order Gp)))  
       ((lagrange_interpolation_poly (zip (PickDistinct I#I) evals) = φ' 
       (lagrange_interpolation_poly (zip (PickDistinct I#I) evals)) = φ' 
            hd evals = poly φ' (PickDistinct I)))"
    proof -
      fix I evals :: "'e mod_ring list"
      fix φ'
      assume asm1: "length I = max_deg  distinct I"
      assume asm2: "evals  set_spmf (map_spmf (map  (of_int_mod_ring  int)) (sample_uniform_list (max_deg+1) (order Gp)))"
      then have evals_length: "length evals = max_deg+1"
        by (force simp add: bind_map_spmf o_def)
      show "(lagrange_interpolation_poly (zip (PickDistinct I#I) evals) = φ' 
       lagrange_interpolation_poly (zip (PickDistinct I#I) evals) = φ'  hd evals = poly φ' (PickDistinct I))"
      proof
        show "lagrange_interpolation_poly (zip (PickDistinct I#I) evals) = φ'  lagrange_interpolation_poly (zip (PickDistinct I#I) evals) = φ'  hd evals = poly φ' (PickDistinct I)"
        proof 
          assume asm: "lagrange_interpolation_poly (zip (PickDistinct I#I) evals) = φ'"
          show "hd evals = poly φ' (PickDistinct I)"
          proof(rule lagrange_interpolation_poly[symmetric, of "zip (PickDistinct I#I) evals"])
            show "distinct (map fst (zip (PickDistinct I#I) evals))"
              using PickDistinct_Prop asm1 distinct_map_fst_zip by blast
            show "φ' = lagrange_interpolation_poly (zip (PickDistinct I#I) evals)"
              using asm[symmetric] .
            show "(PickDistinct I, hd evals)  set (zip (PickDistinct I#I) evals)" 
              using asm1 evals_length
              by (metis Nil_eq_zip_iff add_is_0 hd_in_set hd_zip length_0_conv list.discI list.sel(1) rel_simps(92))
          qed
        qed 
      qed simp
    qed
    show ?thesis 
      unfolding split_def Let_def
      apply (rule spmf_eqI')
      apply (rule unpack_try_spmf)
      apply (rule unpack_bind_spmf'; rule ext)
      apply (rule unpack_bind_spmf'; rule ext)
      apply (rule assert_based_eq)
      apply (rule bind_spmf_cong)
       apply simp
      apply (rule unpack_bind_spmf'; rule ext)
      apply (rule unpack_bind_spmf)
      apply (subst equi)
        apply simp_all
      done
  qed
    text ‹In the next part we split the conjuncture φ = φ' ∧ hd evals = poly φ' (PickDistinct I)›
  into two separate assert statements, so we can use overestimation at a later step to get closer 
  to the DL game.›
  also have " = spmf (TRY do {
  (α, PK)  Setup;
  (I,σ)  𝒜1 PK;
  _ :: unit  assert_spmf (length I = max_deg  distinct I);
  let i = PickDistinct I;
  evals  map_spmf (map  (of_int_mod_ring  int)) (sample_uniform_list (max_deg+1) (order Gp));
  let φ = lagrange_interpolation_poly (zip (i#I) evals);
  let exp_evals = map (λi. g ^ i) evals;
  let C = interpolate_on (zip (i#I) exp_evals) α;
  let witn_tupel = map (λ(x,y). (y,(C  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) (zip I (tl evals));
  φ'  𝒜2 PK σ C I witn_tupel;
  _ :: unit  assert_spmf (φ = φ');
  _ :: unit  assert_spmf (hd evals = poly φ' i);
  return_spmf True
  } ELSE return_spmf False) True"
    by (simp only: assert_collapse)
 also have " = spmf (TRY do {
  (α, PK)  Setup;
  TRY do {
  (I,σ)  𝒜1 PK;
  TRY do {
  _ :: unit  assert_spmf (length I = max_deg  distinct I);
  let i = PickDistinct I;
  TRY do {
  evals  map_spmf (map  (of_int_mod_ring  int)) (sample_uniform_list (max_deg+1) (order Gp));
  let φ = lagrange_interpolation_poly (zip (i#I) evals);
  let exp_evals = map (λi. g ^ i) evals;
  let C = interpolate_on (zip (i#I) exp_evals) α;
  let witn_tupel = map (λ(x,y). (y,(C  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) (zip I (tl evals));
  TRY do {
  φ'  𝒜2 PK σ C I witn_tupel;
  TRY do {
  _ :: unit  assert_spmf (φ = φ');
  TRY do {
  return_spmf (hd evals = poly φ' i)
  } ELSE return_spmf False
  } ELSE return_spmf False
  } ELSE return_spmf False
  } ELSE return_spmf False
  } ELSE return_spmf False
  } ELSE return_spmf False
  } ELSE return_spmf False) True"
   apply (simp only: split_def Let_def)
   apply (subst return_spmf_assert)
   apply (fold try_bind_spmf_lossless2[OF lossless_return_spmf])
   apply (simp del: PickDistinct.simps)
   done
  also have " = spmf (TRY do {
  (α, PK)  Setup;
  (I,σ)  𝒜1 PK;
  _ :: unit  assert_spmf (length I = max_deg  distinct I);
  let i = PickDistinct I;
  evals  map_spmf (map  (of_int_mod_ring  int)) (sample_uniform_list (max_deg+1) (order Gp));
  let φ = lagrange_interpolation_poly (zip (i#I) evals);
  let exp_evals = map (λi. g ^ i) evals;
  let C = interpolate_on (zip (i#I) exp_evals) α;
  let witn_tupel = map (λ(x,y). (y,(C  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) (zip I (tl evals));
  φ'  𝒜2 PK σ C I witn_tupel;
  _ :: unit  assert_spmf (φ = φ');
  return_spmf (hd evals = poly φ' i)
  } ELSE return_spmf False) True"
    unfolding Let_def split_def
    by (fold try_bind_spmf_lossless2[OF lossless_return_spmf])(simp del: PickDistinct.simps)
  also have "  spmf (TRY do {
  (α, PK)  Setup;
  (I,σ)  𝒜1 PK;
  let i = PickDistinct I;
  evals  map_spmf (map  (of_int_mod_ring  int)) (sample_uniform_list (max_deg+1) (order Gp));
  let φ = lagrange_interpolation_poly (zip (i#I) evals);
  let exp_evals = map (λi. g ^ i) evals;
  let C = interpolate_on (zip (i#I) exp_evals) α;
  let witn_tupel = map (λ(x,y). (y,(C  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) (zip I (tl evals));
  φ'  𝒜2 PK σ C I witn_tupel;
  _ :: unit  assert_spmf (φ = φ');
  return_spmf (hd evals = poly φ' i)
  } ELSE return_spmf False) True"
    apply (simp only:Let_def split_def)
    apply (rule try_spmf_le)
    apply (rule bind_spmf_le)+
    apply (simp only: del_assert)
    done
  also have "  spmf (TRY do {
  (α, PK)  Setup;
  (I,σ)  𝒜1 PK;
  let i = PickDistinct I;
  evals  map_spmf (map  (of_int_mod_ring  int)) (sample_uniform_list (max_deg+1) (order Gp));
  let exp_evals = map (λi. g ^ i) evals;
  let C = interpolate_on (zip (i#I) exp_evals) α;
  let witn_tupel = map (λ(x,y). (y,(C  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) (zip I (tl evals));
  φ'  𝒜2 PK σ C I witn_tupel;
  return_spmf (hd evals = poly φ' i)
  } ELSE return_spmf False) True"
    apply (simp only:Let_def split_def)
    apply (rule try_spmf_le)
    apply (rule bind_spmf_le)+
    apply (simp only: del_assert)
    done
    text ‹Next, we split the random sampled list up into a random point concatenated with a 
  one element shorter random list. The random point represents the DL value which creates later 
  the DL instance.›
  also have " = spmf (TRY do {
  (α, PK)  Setup;
  (I,σ)  𝒜1 PK;
  let i = PickDistinct I;
  a  map_spmf (of_int_mod_ring  int) (sample_uniform (order Gp));
  plain_evals  map_spmf (map (of_int_mod_ring  int)) (sample_uniform_list max_deg (order Gp));
  let evals = g ^ a # map (λi. g ^ i) plain_evals;
  let C = interpolate_on (zip (i#I) evals) α;
  let witn_tupel = map (λ(x,y). (y,(C  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) (zip I plain_evals);
  φ'  𝒜2 PK σ C I witn_tupel;
  return_spmf (a = poly φ' i)
  } ELSE return_spmf False) True"
    using pretty_Cons_random_list_split p_gr_two CARD_Gp 
    by(simp add: bind_map_spmf o_def Let_def del: PickDistinct.simps)
  text ‹Lastly, we extract the functions from the reduction adversary into an do-block to mirror 
  the DL game.›
  also have " = spmf (TRY do {
  a  map_spmf (of_int_mod_ring  int) (sample_uniform (order Gp));
  (α, PK)  Setup;
  (I,σ)  𝒜1 PK;
  let i = PickDistinct I;
  plain_evals  map_spmf (map (of_int_mod_ring  int)) (sample_uniform_list max_deg (order Gp));
  let evals = g ^ a # map (λi. g ^ i) plain_evals;
  let C = interpolate_on (zip (i#I) evals) α;
  let witn_tupel = map (λ(x,y). (y,(C  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) (zip I plain_evals);
  φ'  𝒜2 PK σ C I witn_tupel;
  return_spmf (a = poly φ' i)
  } ELSE return_spmf False) True"
    apply (simp only: Let_def split_def)
    apply (subst bind_commute_spmf)
    apply (subst bind_commute_spmf)
    apply (blast)
    done
  also have " = spmf (TRY do {
  a  map_spmf (of_int_mod_ring  int) (sample_uniform (order Gp));
  a'  do {
  (α, PK)  Setup;
  (I,σ)  𝒜1 PK;
  let i = PickDistinct I;
  plain_evals  map_spmf (map (of_int_mod_ring  int)) (sample_uniform_list max_deg (order Gp));
  let evals = g ^ a # map (λi. g ^ i) plain_evals;
  let C = interpolate_on (zip (i#I) evals) α;
  let witn_tupel = map (λ(x,y). (y,(C  ÷GpgGp^Gpy) ^Gp(1/(α-x)))) (zip I plain_evals);
  φ'  𝒜2 PK σ C I witn_tupel;
  return_spmf (poly φ' i)};
  return_spmf (a =a')
  } ELSE return_spmf False) True"
    by (simp add: Let_def split_def o_def del: PickDistinct.simps)
   also have " = DL_Gp.advantage (reduction 𝒜1 𝒜2)"
    unfolding DL_Gp.advantage_def DL_Gp.game_alt_def2 reduction.simps ..
  finally show ?thesis .
qed

text ‹Finally we assemble all proof steps for the weak hiding theorem›
theorem weak_hiding: 
  assumes lossless_𝒜1: "PK . lossless_spmf (𝒜1 PK)"
  and lossless_𝒜2: "PK σ C I W . lossless_spmf (𝒜2 PK σ C I W)"
  shows "weak_eval_hiding_advantage 𝒜1 𝒜2  
   DL_Gp.advantage (reduction 𝒜1 𝒜2) + t_DL_Gp.advantage (reduction_tDL 𝒜1)"
proof -
  have "weak_eval_hiding_advantage 𝒜1 𝒜2 = spmf (game1 𝒜1 𝒜2) True"
    using hiding_game_to_game1
    unfolding weak_eval_hiding_advantage_def eval_hiding_advantage_def 
      weak_eval_hiding_adversary1_def weak_eval_hiding_game_def .
  also have "  spmf (game2 𝒜1 𝒜2) True + t_DL_Gp.advantage (reduction_tDL 𝒜1)"
    using assms fundamental_lemma_game1_game2 by blast
  also have "  DL_Gp.advantage (reduction 𝒜1 𝒜2) + t_DL_Gp.advantage (reduction_tDL 𝒜1)"
    by (simp add: game2_le_DL)
  finally show ?thesis .
qed

end

end