Theory Binary_Orthogonal

subsection ‹Binary orthogonality is Diophantine›

theory Binary_Orthogonal
  imports Binomial_Coefficient "Digit_Expansions.Binary_Operations" "Lucas_Theorem.Lucas_Theorem"
begin

lemma equiv_with_lucas: "nth_digit = Lucas_Theorem.nth_digit_general"
  unfolding nth_digit_def Lucas_Theorem.nth_digit_general_def by simp

lemma lm0241_ortho_binom_equiv:"(a  b)  odd ((a + b) choose b)" (is "?P  ?Q")
proof
  assume "?P"
  hence dig0:"(i. (nth_bit a i) * (nth_bit b i) = 0)"
    using ortho_mult_equiv
    by auto
  hence "(i. (nth_bit a i) * (nth_bit b i)  1)"
    by presburger
  hence dcons:"(i. ¬(((nth_bit a i) = 1)  ((nth_bit b i) = 1)))"
    by auto
  hence "(i. (bin_carry a b i) = 0)" using no_carry_mult_equiv dig0
    by blast
  hence dsum:"(i. (nth_bit (a + b) i) = (nth_bit a i) + (nth_bit b i))"
    by (metis One_nat_def add.commute add_cancel_right_left add_self_mod_2
        dig0 mult_is_0 not_mod2_eq_Suc_0_eq_0 nth_bit_def one_mod_two_eq_one
        sum_digit_formula)

  have bdd_ab_exists:"(p. (a + b) < 2^(Suc p))"
    using aux1_lm0241_pow2_up_bound by auto
  then obtain p where bdd_ab:"(a + b) < 2^(Suc p)" by blast
  moreover from bdd_ab have "b < 2^(Suc p)" by auto

  ultimately have "((a + b) choose b) mod 2 =
        (ip. ((nth_digit (a + b) i 2) choose (nth_digit b i 2))) mod 2"
    using lucas_theorem[of "a+b" 2 p b] bdd_ab two_is_prime_nat
    by (simp add: equiv_with_lucas)

  then have a_choose_b_digit_prod: "((a + b) choose b) mod 2 =
        (ip. ((nth_bit (a + b) i) choose (nth_bit b i))) mod 2"
    using nth_digit_base2_equiv
    by (auto cong: prod.cong)

  have "(i. ((nth_bit (a + b) i) choose (nth_bit b i) = 1))"
    using aux2_lm0241_single_digit_binom[where ?a="nth_bit (a + b) i"
                                               and ?b="nth_bit b i"]
    by (metis add.commute add.right_neutral binomial_n_0 binomial_n_n dig0 dsum
        mult_is_0)
  hence f0:"1 = (i<p. (nth_bit (a + b) i) choose (nth_bit b i))"
    by simp
  hence f1:"... = ... mod 2" by simp
  hence f2:"... = ((a + b) choose b) mod 2"
    using a_choose_b_digit_prod by (simp add: i. (a + b) ¡ i choose b ¡ i = 1)
  then show "?Q" using f0 by fastforce
next
  assume "?Q"
  hence a_choose_b_odd:"((a + b) choose b) mod 2 = 1"
    using odd_iff_mod_2_eq_one by blast

  have bdd_ab_exists:"(p. (a + b) < 2^(Suc p))"
    using aux1_lm0241_pow2_up_bound by auto
  then obtain p where bdd_ab:"(a + b) < 2^(Suc p)" by blast
  moreover from bdd_ab have bdd_b: "b < 2^(Suc p)" by auto

  ultimately have "((a + b) choose b) mod 2 =
        (ip. ((nth_digit (a + b) i 2) choose (nth_digit b i 2))) mod 2"
    using lucas_theorem[of "a+b" 2 p b] bdd_ab two_is_prime_nat
    by (simp add: equiv_with_lucas)

  then have a_choose_b_digit_prod: "((a + b) choose b) mod 2 =
        (ip. ((nth_bit (a + b) i) choose (nth_bit b i))) mod 2"
    using nth_digit_base2_equiv nth_digit_def
    by (auto cong: prod.cong)
  hence all_prod_one_mod2:"... = 1" using a_choose_b_odd by linarith

  have choose_bdd:"(i. 1  (nth_bit (a + b) i) choose (nth_bit b i))"
    using nth_bit_bounded
    by (metis One_nat_def binomial_n_0 choose_one not_mod2_eq_Suc_0_eq_0
        nth_bit_def order_refl)
  hence "1  (ip. ((nth_bit (a + b) i) choose (nth_bit b i)))"
    using all_prod_one_mod2 by (meson prod_le_1 zero_le)
  hence "(ip. ((nth_bit (a + b) i) choose (nth_bit b i))) mod 2 =
             (ip. ((nth_bit (a + b) i) choose (nth_bit b i)))"
    using all_prod_one_mod2 by linarith
  hence "... = 1"
    using all_prod_one_mod2 by linarith
  hence sub_pq_one:"ip. (nth_bit (a + b) i) choose (nth_bit b i) = 1"
    using
      aux4_lm0241_prod_one[where ?n="p" and ?f="(λi. (nth_bit (a + b) i) choose (nth_bit b i))"]
      choose_bdd by blast

  have "r > p. (a + b) < 2^r" using bdd_ab
    by(metis One_nat_def Suc_lessI lessI less_imp_add_positive numeral_2_eq_2
       power_strict_increasing_iff trans_less_add1)
  hence "r > p. r  p  (a + b) < 2^r" by auto
  hence ab_digit_bdd:"r > p. r  p  nth_bit (a + b) r = 0"
    using nth_bit_def by simp

  have "k > p. b < 2 ^ k" using bdd_b
    by(metis One_nat_def Suc_lessI lessI less_imp_add_positive numeral_2_eq_2
       power_strict_increasing_iff trans_less_add1)
  hence b_digit_bdd:"k > p. k  p  nth_bit b k = 0"
    using nth_bit_def
    by (simp add: k>p. b < 2 ^ k)

  from b_digit_bdd ab_digit_bdd aux3_lm0241_binom_bounds
  have "i. i  p  (nth_bit (a + b) i) choose (nth_bit b i) = 1"
    by (simp add: le_less sub_pq_one)

  hence "i. ((nth_bit (a + b) i) choose (nth_bit b i)) = 1"
    using sub_pq_one not_less by (metis linear)
  hence "i. ¬(nth_bit a i = 1  nth_bit b i = 1)" using aux5_lm0241 by blast
  hence "i. ((nth_bit a i = 0  nth_bit b i = 1) 
                  (nth_bit a i = 1  nth_bit b i = 0) 
                  (nth_bit a i = 0  nth_bit b i = 0))"
    by (auto simp add:nth_bit_def nth_digit_bounded; metis nat.simps(3))
  hence "i. (nth_bit a i) * (nth_bit b i) = 0" using mult_is_0 by blast
  then show "?P" using ortho_mult_equiv by blast
qed

definition orthogonal (infix "[⊥]" 50)
  where "P [⊥] Q  (BINARY (λa b. a  b) P Q)"

lemma orthogonal_dioph[dioph]:
  fixes P Q
  defines "DR  (P [⊥] Q)"
  shows "is_dioph_rel DR"
proof -
  define P' Q' where pushed_def: "P'  push_param P 1" "Q'  push_param Q 1"

  define DS where "DS  [∃] [Param 0 = (P' [+] Q') choose Q'] [∧] ODD (Param 0)"

  have "eval DS a = eval DR a" for a
    unfolding DS_def DR_def orthogonal_def pushed_def defs
    using push_push1 lm0241_ortho_binom_equiv by (simp add: push0)

  moreover have "is_dioph_rel DS"
    unfolding DS_def by (simp add: dioph)

  ultimately show ?thesis
    by (auto simp: is_dioph_rel_def)
qed

declare orthogonal_def[defs]

end