Theory RandomXORHashFamily

section ‹Random XOR hash family›

text ‹This section defines a hash family based on random XORs and proves
  that this hash family is 3-universal. ›

theory RandomXORHashFamily imports
  RandomXOR
begin

lemma finite_dom:
  assumes "finite V"
  shows "finite {w :: 'a  bool. dom w = V}"
proof -
  have *: "{w :: 'a  bool. dom w = V} =
    {w. dom w = V  ran w  {True,False}}"
    by auto
  show ?thesis unfolding *
    apply (intro finite_set_of_finite_maps)
    using assms by auto
qed

lemma xor_hash_eq_dom:
  assumes "xor_hash ω xors = α"
  shows "dom xors = dom α"
  using assms unfolding xor_hash_def
  by auto

lemma prob_random_xors_xor_hash_indicat_real:
  assumes V: "finite V"
  shows"
  measure_pmf.prob (random_xors V n)
    {xors. xor_hash ω xors = α} =
     indicat_real {α::nat  bool. dom α = {0..<n}} α /
     real (card {α::nat  bool. dom α = {0..<n}})"
proof -
  have *: "{α::nat  bool. dom α = {0..<n}} =
     {α. dom α = {0..<n}  ran α  {True, False}}"
    by auto
  have **: "card {α::nat  bool. dom α = {0..<n}} = 2^n"
    unfolding *
    apply (subst card_dom_ran)
    by (auto simp add: numerals(2))
  have "dom α = {..<n}  dom α  {..<n}"
    by auto
  moreover {
    assume "dom α = {..<n}"
    from prob_random_xors_xor_hash[OF V this]
    have ?thesis
      unfolding **
      by (simp add: dom α = {..<n} atLeast0LessThan)
  }
  moreover {
    assume *:"dom α  {..<n}"
    then have "x  set_pmf (random_xors V n) 
         α  xor_hash ω x" for x
      by (metis (mono_tags, lifting) V mem_Collect_eq random_xors_set_pmf xor_hash_eq_dom)
    then have "measure_pmf.prob (random_xors V n)
     {xors. xor_hash ω xors = α} = 0"
      apply (intro measure_pmf.measure_exclude[where A = "set_pmf ((random_xors V n))"])
      by (auto simp add: Sigma_Algebra.measure_def emeasure_pmf xor_hash_eq_dom)
    then have ?thesis
      by (simp add: * atLeast0LessThan)
  }
  ultimately show ?thesis
    by auto
qed

lemma xor_hash_family_uniform:
  assumes V: "finite V"
  assumes "dom ω = V"
  shows "prob_space.uniform_on
         (random_xors V n)
         (xor_hash i) {α. dom α = {0..<n}}"
  apply (intro measure_pmf.uniform_onI[where p = "random_xors V n"])
  subgoal by clarsimp
  subgoal  using finite_dom by blast
  using prob_random_xors_xor_hash_indicat_real[OF V]
  by (auto intro!: exI[where x = "(λi. if i < n then Some True else None)"] split:if_splits)

lemma random_xors_xor_hash_pair_indicat:
  assumes V: "finite V"
  assumes ω: "dom ω = V"
  assumes ω': "dom ω' = V"
  assumes neq: "ω  ω'"
  shows "
  measure_pmf.prob (random_xors V n)
    {xors.
      xor_hash ω xors = α  xor_hash ω' xors = α'} =
  (measure_pmf.prob (random_xors V n)
    {xors.
      xor_hash ω xors = α} *
   measure_pmf.prob (random_xors V n)
    {xors.
      xor_hash ω' xors = α'})"
proof -
  have "dom α = {..<n}  dom α' = {..<n} 
    ¬(dom α = {..<n}  dom α' = {..<n})" by auto
  moreover {
    assume *: "dom α = {..<n}" "dom α' = {..<n}"
    have "measure_pmf.prob (random_xors V n)
     {xors.
      xor_hash ω xors = α  xor_hash ω' xors = α'} = 1 / 4 ^n"
      by (simp add: "*"(1) "*"(2) assms(1) assms(2) assms(3) assms(4) random_xors_xor_hash_pair)
    moreover have "
    measure_pmf.prob (random_xors V n)
      {xors.  xor_hash ω xors = α} = 1/2^n"
      by (simp add: "*"(1) assms(1) prob_random_xors_xor_hash)
    moreover have "
    measure_pmf.prob (random_xors V n)
      {xors.  xor_hash ω' xors = α'} = 1/2^n"
      by (simp add: "*"(2) assms(1) prob_random_xors_xor_hash)
    ultimately have ?thesis
      by (metis (full_types) Groups.mult_ac(2) four_x_squared power2_eq_square power_mult power_one_over verit_prod_simplify(2))
  }
  moreover {
    assume *: "dom α  {..<n}  dom α'  {..<n}"
    then have **: "x  set_pmf (random_xors V n) 
         α = xor_hash ω x 
         α' = xor_hash ω' x  False" for x
      by (metis (mono_tags, lifting) CollectD assms(1) random_xors_set_pmf xor_hash_eq_dom)
    have "measure_pmf.prob (random_xors V n)
    {xors.
      xor_hash ω xors = α} = 0 
     measure_pmf.prob (random_xors V n)
      {xors.
        xor_hash ω' xors = α'} = 0"
      unfolding prob_random_xors_xor_hash_indicat_real[OF V]
      by (metis (full_types) * atLeast0LessThan div_0 indicator_simps(2) mem_Collect_eq)
    moreover have "
    measure_pmf.prob (random_xors V n)
    {xors.
      xor_hash ω xors = α  xor_hash ω' xors = α'} = 0"
      apply (intro measure_pmf.measure_exclude[where A = "set_pmf ((random_xors V n))"])
      using ** by (auto simp add: Sigma_Algebra.measure_def emeasure_pmf)
    ultimately have ?thesis by auto
  }
  ultimately show ?thesis by auto
qed

lemma prod_3_expand:
  assumes "a  b" "b  c" "c  a"
  shows"(ω{a, b, c}. f ω) = f a * (f b * f c)"
  using assms by auto

lemma random_xors_xor_hash_three_indicat:
  assumes V: "finite V"
  assumes ω: "dom ω = V"
  assumes ω': "dom ω' = V"
  assumes ω'': "dom ω'' = V"
  assumes neq: "ω  ω'" "ω'  ω''" "ω''  ω"
  shows "
  measure_pmf.prob (random_xors V n)
    {xors.
      xor_hash ω xors = α
     xor_hash ω' xors = α'
     xor_hash ω'' xors = α''} =
  (measure_pmf.prob (random_xors V n)
    {xors.
      xor_hash ω xors = α} *
   measure_pmf.prob (random_xors V n)
    {xors.
      xor_hash ω' xors = α'} *
   measure_pmf.prob (random_xors V n)
    {xors.
      xor_hash ω'' xors = α''})"
proof -
  have "dom α = {..<n}  dom α' = {..<n}  dom α'' = {..<n} 
    ¬(dom α = {..<n}  dom α' = {..<n}  dom α'' = {..<n})" by auto
  moreover {
    assume *: "dom α = {..<n}" "dom α' = {..<n}" "dom α'' = {..<n}"
    have 1:"measure_pmf.prob (random_xors V n)
     {xors.
      xor_hash ω xors = α 
      xor_hash ω' xors = α' 
      xor_hash ω'' xors = α''} = 1 / 8 ^n"
      apply (intro random_xors_xor_hash_three)
      using V * ω ω' ω'' neq by auto
    have 2:"
    measure_pmf.prob (random_xors V n)
      {xors.  xor_hash ω xors = α} = 1/2^n"
      by (simp add: "*"(1) assms(1) prob_random_xors_xor_hash)
    have 3:"
    measure_pmf.prob (random_xors V n)
      {xors.  xor_hash ω' xors = α'} = 1/2^n"
      by (simp add: "*"(2) assms(1) prob_random_xors_xor_hash)
    have 4: "
    measure_pmf.prob (random_xors V n)
      {xors.  xor_hash ω'' xors = α''} = 1/2^n"
      by (simp add: "*"(3) assms(1) prob_random_xors_xor_hash)
    have ?thesis
      unfolding 1 2 3 4
      by (metis (mono_tags, opaque_lifting) arith_simps(11) arith_simps(12) arith_simps(58) divide_divide_eq_left mult.right_neutral power_mult_distrib times_divide_eq_right)
  }
  moreover {
    assume *: "dom α  {..<n}  dom α'  {..<n}  dom α''  {..<n}"
    then have **: "x  set_pmf (random_xors V n) 
         α = xor_hash ω x 
         α' = xor_hash ω' x 
         α'' = xor_hash ω'' x  False" for x
      by (metis (mono_tags, lifting) CollectD assms(1) random_xors_set_pmf xor_hash_eq_dom)
    have "measure_pmf.prob (random_xors V n)
    {xors.
      xor_hash ω xors = α} = 0 
     measure_pmf.prob (random_xors V n)
      {xors.
        xor_hash ω' xors = α'} = 0 
     measure_pmf.prob (random_xors V n)
      {xors.
        xor_hash ω'' xors = α''} = 0"
      unfolding prob_random_xors_xor_hash_indicat_real[OF V]
      by (metis (full_types) * atLeast0LessThan div_0 indicator_simps(2) mem_Collect_eq)
    moreover have "
    measure_pmf.prob (random_xors V n)
    {xors.
      xor_hash ω xors = α  xor_hash ω' xors = α'  xor_hash ω'' xors = α''} = 0"
      apply (intro measure_pmf.measure_exclude[where A = "set_pmf ((random_xors V n))"])
      using ** by (auto simp add: Sigma_Algebra.measure_def emeasure_pmf)
    ultimately have ?thesis by auto
  }
  ultimately show ?thesis
    by fastforce
qed

lemma xor_hash_3_indep:
  assumes V: "finite V"
  assumes J: "card J  3" "J  {α. dom α = V}"
  shows "
    measure_pmf.prob (random_xors V n)
      {xors. ωJ. xor_hash ω xors = f ω} =
       (ωJ.
          measure_pmf.prob (random_xors V n)
           {xors. xor_hash ω xors = f ω})"
proof -
  have "card J = 0  card J = 1  card J = 2  card J = 3"
    using assms by auto
  moreover {
    assume" card J = 0"
    then have "J = {}"
      by (meson assms(1) assms(3) card_eq_0_iff finite_dom finite_subset)
    then have ?thesis
      by clarsimp
  }
  moreover {
    assume "card J = 1"
    then obtain x where "J = {x}"
      using card_1_singletonE by blast
    then have ?thesis
      by auto
  }
  moreover {
    assume "card J = 2"
    then obtain ω ω' where J:"J = {ω, ω'}" and
      ω: "ω  ω'" "dom ω = V" "dom ω' = V"
      unfolding card_2_iff
      using J
      by force
    have ?thesis unfolding J
      by (auto simp add: random_xors_xor_hash_pair_indicat V ω)
  }
  moreover {
    assume "card J = 3"
    then obtain ω ω' ω'' where J:"J = {ω, ω', ω''}" and
      ω: "ω  ω'" "ω'  ω''" "ω''  ω"
        "dom ω = V" "dom ω' = V" "dom ω'' = V"
      unfolding card_3_iff
      using J
      by force
    have ?thesis unfolding J
      by (auto simp add: random_xors_xor_hash_three_indicat V ω  prod_3_expand[OF ω(1-3)])
  }
  ultimately show ?thesis by auto
qed

lemma xor_hash_3_wise_indep:
  assumes "finite V"
  shows "prob_space.k_wise_indep_vars
     (random_xors V n) 3
     (λ_. Universal_Hash_Families_More_Independent_Families.discrete) xor_hash
     {α. dom α = V}"
  apply (subst prob_space.k_wise_indep_vars_def)
  by (auto intro!: measure_pmf.indep_vars_pmf xor_hash_3_indep simp add: measure_pmf.prob_space_axioms assms card_mono dual_order.trans)

theorem xor_hash_family_3_universal:
  assumes "finite V"
  shows"prob_space.k_universal
    (random_xors V n) 3 xor_hash
    {α::'a  bool. dom α = V}
    {α::nat  bool. dom α = {0..<n}}"
  apply (subst prob_space.k_universal_def)
  subgoal by (clarsimp simp add: measure_pmf.prob_space_axioms )
  using xor_hash_3_wise_indep assms xor_hash_family_uniform assms by blast

corollary xor_hash_family_2_universal:
  assumes "finite V"
  shows"prob_space.k_universal
    (random_xors V n) 2 xor_hash
    {α::'a  bool. dom α = V}
    {α::nat  bool. dom α = {0..<n}}"
  using assms
  by (auto intro!: prob_space.k_universal_mono[OF _ _ xor_hash_family_3_universal] measure_pmf.prob_space_axioms)

end