Theory Towards_Coppersmith

section ‹ Proof of Lightweight Algorithm ›

text ‹ We start by proving the "lightweight algorithm" as a
  stepping stone to the full algorithm (proved in Coppersmith.thy). ›

theory Towards_Coppersmith

imports Coppersmith_Generic
begin

subsection ‹ Basic properties ›

lemma dim_form_basis_helper:
  shows "length (form_basis_helper p M X d) = d + 1"
  unfolding form_basis_helper_def 
  by auto

lemma dim_form_basis:
  shows "length (form_basis p M X d) = d + 2"
  using dim_form_basis_helper form_basis_def by simp

subsection ‹ Matrix properties ›

subsubsection ‹ Basic properties and preliminaries ›

lemma dim_row_basis_mat:
  assumes "degree p  1"
  shows "dim_row (vec_list_to_square_mat (form_basis p M X (degree p - 1))) = degree p + 1"
proof - 
  have "length (form_basis p M X (degree p - 1)) = degree p + 1"
    using assms dim_form_basis[of p M X "degree p - 1"] 
    by auto 
  then show ?thesis
    unfolding vec_list_to_square_mat_def mat_of_rows_list_def by auto
qed

lemma dim_col_basis_mat:
  assumes "degree p  1"
  shows"dim_col (vec_list_to_square_mat (form_basis p M X (degree p - 1))) = (degree p + 1)"
proof - 
  have "length (form_basis p M X (degree p - 1)) = degree p + 1"
    using assms dim_form_basis[of p M X "degree p - 1"] 
    by auto 
  then show ?thesis unfolding vec_list_to_square_mat_def
    by auto
qed

lemma matrix_carrier:
  assumes "degree p  1"
  assumes "i < degree p + 1"
  shows "vec_list_to_square_mat (form_basis p M X (degree p - 1))  carrier_mat (degree p + 1) (degree p + 1)"
  using dim_row_basis_mat[of p M X] dim_col_basis_mat[of p M X] assms 
  by auto

lemma vector_sum_monom:
  fixes v:: "int vec"
  fixes d i:: "nat"
  assumes "d = dim_vec v"
  assumes "d > 0"
  assumes i_lt: "i < d"
  assumes zero_monom: "j::nat. j < d  j  i  monom (v $ j) j = 0"
  shows "(i< d. monom (v $ i) i) = monom (v $ i) i"
proof - 
  have "(i< d.  monom (v $ i) i) = (i{0..<d}.  monom (v $ i) i)"
    using atLeast_upt set_upt by presburger
  then have sum_split: "(i< d.  monom (v $ i) i) = (i{0..<d}-{i}.  monom (v $ i) i) +  monom (v $ i) i"
    using i_lt 
    by (metis (no_types, lifting) Groups.add_ac(2) atLeast0LessThan finite_lessThan lessThan_iff sum.remove)
  have "j::nat. j  {0..<d}-{i}  monom (v $ j) j = 0"
    using zero_monom i_lt by simp
  then have "(i{0..<d}-{i}.  monom (v $ i) i) = (i{0..<d}-{i}. 0)"
    by (simp add: sum.neutral)
  then show ?thesis using sum_split by simp
qed

lemma int_poly_associated_to_g_i_vec:
  assumes X_gt: "X > 0"
  assumes M_gt: "M > 0"
  assumes i_lt: "i  (degree p)"
  shows "int_poly_associated_to_vec (g_i_vec M X i (degree p + 1)) X = monom M i"
proof - 
  let ?gi = "g_i_vec M X i (degree p + 1)"
  let ?d = "degree p"
  let ?newvec = "vec (dim_vec (g_i_vec M X i (degree p + 1)))
            (λj. real_of_int (g_i_vec M X i (degree p + 1) $ j) / real (X ^ j))"
  have dim_vec_gi: "dim_vec ?gi = degree p + 1"
    unfolding g_i_vec_def by simp
  then have dim_vec_newvec: "dim_vec ?newvec = ?d + 1"
    by simp
  then have newvec_j: "j. j < ?d+1  j  i  ?newvec $ j = 0"
    unfolding g_i_vec_def by simp
  then have monom_zero: "j. j < ?d+1  j  i  monom (?newvec $ j) j = 0"
    by blast
  have poly_monom_sum: "(i< ?d+1.
           monom (?newvec $ i) i) = monom (?newvec $ i) i"
    using vector_sum_monom[of "?d+1" ?newvec i] dim_vec_newvec monom_zero assms(3)
    by simp
  have newvec_i: "?newvec $ i = M"
    using X_gt M_gt i_lt dim_vec_newvec unfolding g_i_vec_def by simp
  then have monom_i: "monom M i = monom (?newvec $ i) i"
    by auto
  show ?thesis
    using monom_i poly_monom_sum dim_vec_newvec
    unfolding int_poly_associated_to_vec_def by auto
qed

lemma ith_row_form_basis:
  shows "i  d  ((form_basis p M X d)!i) = (form_basis_helper p M X d)!i"
        "((form_basis p M X d)!(d+1)) = vec_associated_to_int_poly p X"
  using dim_form_basis_helper[of p M X d] form_basis_def
   apply (simp add: append_Cons_nth_left)
  using dim_form_basis_helper[of p M X d] form_basis_def
  by (simp add: append_Cons_nth(1) append_Cons_nth_left)

lemma set_form_basis:
  shows "x set (form_basis p M X (degree p - 1))  x = vec_associated_to_int_poly p X 
  x  set (form_basis_helper p M X (degree p - 1))"
  using ith_row_form_basis ith_row_form_basis_helper dim_form_basis
    form_basis_def
  by simp 

lemma dim_vector_in_basis:
  fixes i:: "nat"
  assumes "i < d + 2"
  shows "dim_vec ((form_basis p M X d) ! i) = (degree p+1)"
proof -
  let ?vec = "(form_basis p M X d) ! i"
  have eo: "List.member (form_basis_helper p M X d) ?vec  ?vec = vec_associated_to_int_poly p X"
    using assms  
    by (metis List.member_def add_Suc_right dim_form_basis_helper form_basis_def less_Suc_eq nat_1_add_1 nth_append nth_append_length nth_mem plus_1_eq_Suc) 
  have len_helper: "length (form_basis_helper p M X d) = d + 1"
    using dim_form_basis form_basis_def by simp
  have h1: "x. List.member (form_basis_helper p M X d) x  dim_vec x = degree p + 1"
  proof - 
    fix x 
    assume "List.member (form_basis_helper p M X d) x"
    then have "k. x = g_i_vec M X k (degree p + 1)"
      using ith_row_form_basis_helper
      by (metis List.member_def dim_form_basis_helper in_set_conv_nth le_simps(2) semiring_norm(174)) 
    then show "dim_vec x = degree p + 1"
      using assms unfolding g_i_vec_def by auto
  qed 
  have h2: "dim_vec (vec_associated_to_int_poly p X) = degree p + 1"
    using assms dim_vec_vec_associated_to_poly
    by auto
  show ?thesis
    using h1 h2 eo by auto 
qed


subsubsection ‹ Properties of matrix associated to input ›

lemma matrix_row_form_basis_carrier:
  assumes "degree p  1"
  assumes "i < degree p + 1"
  shows "form_basis p M X (degree p - 1) ! i  carrier_vec (degree p + 1)"
proof - 
  show ?thesis
    using assms  dim_vector_in_basis[of i " degree p - 1" p M X]
    unfolding carrier_vec_def by auto 
qed

lemma matrix_row_form_basis:
  assumes "degree p  1"
  assumes i_lt: "i < degree p + 1"
  shows "row (vec_list_to_square_mat (form_basis p M X (degree p - 1))) i = (form_basis p M X (degree p - 1)) ! i"
  using dim_form_basis[of p M X "degree p - 1"]
  by (metis add_Suc_right arith_special(3) assms(1) assms(2) le_add_diff_inverse mat_of_rows_row matrix_row_form_basis_carrier plus_1_eq_Suc semiring_norm(174) vec_list_to_square_mat_def)

lemma matrix_diagonal_element:
  assumes "degree p  1"
  assumes "i < degree p"
  shows "vec_list_to_square_mat
                             (form_basis p M X (degree p - 1)) $$
                            (i, i) = ((form_basis p M X (degree p - 1)) ! i)$i"
  using assms 
  by (metis dim_col_basis_mat dim_row_basis_mat index_row(1) less_Suc_eq matrix_row_form_basis semiring_norm(174))

lemma no_zeros_on_diagonal:
  assumes "degree p  1"
  assumes "M > 0"
  assumes "X > 0"
  shows "0  set (diag_mat (vec_list_to_square_mat (form_basis p M X (degree p - 1))))"
proof - 
  have eval_elt: "i. i < degree p + 1  vec_list_to_square_mat (form_basis p M X (degree p - 1)) $$ (i, i) 
  = ((form_basis p M X (degree p - 1))!i)$i"
  proof - 
    fix i
    assume "i < degree p + 1"
    then show "vec_list_to_square_mat (form_basis p M X (degree p - 1)) $$ (i, i) 
  = ((form_basis p M X (degree p - 1))!i)$i"
      using assms dim_form_basis dim_row_basis_mat dim_vector_in_basis index_row(1)
      by (metis dim_col_basis_mat matrix_row_form_basis)
  qed
  have "i. i < degree p + 1  
    ((form_basis p M X (degree p - 1))!i)$i  0"
  proof - 
    fix i
    assume i_lt: "i < degree p + 1"
    {assume *: "i < degree p"
      then have "((form_basis p M X (degree p - 1))!i) = (form_basis_helper p M X (degree p - 1))!i"
        by (metis add_2_eq_Suc' add_diff_cancel_left' assms(1) dim_form_basis form_basis_def length_append_singleton nth_append ordered_cancel_comm_monoid_diff_class.add_diff_inverse plus_1_eq_Suc)
      then have "((form_basis p M X (degree p - 1))!i)$i  0"
        using no_zeros_on_diagonal_helper assms "*" 
        by (metis bot_nat_0.not_eq_extremum mult_eq_0_iff of_nat_eq_0_iff power_eq_0_iff)
    }
    moreover {assume *: "i = degree p"
      then have eq: "((form_basis p M X (degree p - 1))!i) = vec_associated_to_int_poly p X"
        by (metis assms(1) dim_form_basis_helper form_basis_def nth_append_length ordered_cancel_comm_monoid_diff_class.diff_add)
      have "coeff p (degree p)  0"
        using assms(1) by force
      then have "((form_basis p M X (degree p - 1))!i)$i  0"
        using * eq assms unfolding vec_associated_to_poly_def
        by auto
    }
    ultimately show "((form_basis p M X (degree p - 1))!i)$i  0"
      using i_lt  
      by linarith
  qed
  then have imp_false: "(i < degree p + 1. 
    (vec_list_to_square_mat (form_basis p M X (degree p - 1)) $$ (i, i))
     = 0)  False"
    by (metis eval_elt)
  then have "0  set (map (λi. vec_list_to_square_mat
                        (form_basis p M X (degree p - 1)) $$
                       (i, i)) [0..< degree p + 1])  False"
  proof - 
    assume "0  set (map (λi. vec_list_to_square_mat
                        (form_basis p M X (degree p - 1)) $$
                       (i, i)) [0..< degree p + 1])"
    then have "(i::nat) <  degree p + 1. vec_list_to_square_mat
                        (form_basis p M X (degree p - 1)) $$
                       (i, i) = 0"
      using less_SucI less_Suc_eq 
      by auto
    then show "False" using imp_false 
      by auto
  qed
  then show ?thesis
    unfolding diag_mat_def
    using dim_row_basis_mat assms
    by auto 
qed

lemma form_basis_helper_is_lower_triangular:
  fixes i j:: "nat"
  assumes "i < j"
  assumes "j < (degree p + 1)"
  shows "((form_basis_helper p M X (degree p - 1))!i)$j = 0"
proof - 
  have "(form_basis_helper p M X (degree p - 1))!i = g_i_vec M X i (degree p + 1)"
    using ith_row_form_basis_helper assms 
    by (smt (verit, best) Suc_diff_Suc diff_is_0_eq le_add_diff_inverse nat_SN.gt_trans nat_le_linear nat_less_le plus_1_eq_Suc semiring_norm(174))
  then show ?thesis unfolding g_i_vec_def using assms
    by force
qed

lemma form_basis_is_lower_triangular:
  fixes i j::"nat"
  assumes i_lt: "i < j"
  assumes j_lt: "j < (degree p + 1)"
  shows "(vec_list_to_square_mat (form_basis p M X (degree p - 1))) $$ (i,j) = 0"
proof - 
  let ?M = "vec_list_to_square_mat (form_basis p M X (degree p - 1))"
  have M_elt: "?M $$ (i, j) = ((form_basis p M X (degree p - 1))!i)$j"
    by (smt (verit) Suc_eq_plus1 Suc_pred assms(1) assms(2) dim_col_basis_mat dim_row_basis_mat index_row(1) le_add1 less_Suc0 matrix_row_form_basis nat_SN.gt_trans nat_neq_iff plus_1_eq_Suc)
  {assume *: "i < degree p"
    then have "(form_basis p M X (degree p - 1))!i = (form_basis_helper p M X (degree p - 1))!i"
      by (simp add: form_basis_def dim_form_basis_helper nth_append)
    then have "((form_basis p M X (degree p - 1))!i)$j = ((form_basis_helper p M X (degree p - 1))!i)$j"
      by auto
    then have "((form_basis p M X (degree p - 1))!i)$j = 0"
      using form_basis_helper_is_lower_triangular[OF i_lt j_lt] assms * 
      by auto
  } moreover {
    assume *: "i = degree p"
    have " (form_basis p M X (degree p - 1))!i = vec_associated_to_int_poly p X"
      using "*" assms(1) assms(2) by linarith
    then have "((form_basis p M X (degree p - 1))!i)$j = 0"
      using assms 
      by (metis "*" Suc_eq_plus1 not_less_eq)
  }
  ultimately have "((form_basis p M X (degree p - 1))!i)$j = 0"
    using assms(1) assms(2) by linarith
  then show ?thesis using M_elt 
    by auto 
qed





lemma form_basis_distinct:
  assumes "degree p  1"
  assumes "M > 0"
  assumes "X > 0"
  shows "distinct (form_basis p M X (degree p - 1))"
proof - 
  let ?M = "vec_list_to_square_mat (form_basis p M X (degree p - 1))"
  let ?dim = "degree p + 1"
  have rows_eq: "rows ?M = form_basis p M X (degree p - 1)"
    by (smt (verit, best) assms(1) dim_row_basis_mat in_set_conv_nth mat_of_rows_carrier(2) matrix_row_form_basis_carrier rows_mat_of_rows subset_code(1) vec_list_to_square_mat_def)
  have in_set:"?M  carrier_mat ?dim ?dim"
    using dim_row_basis_mat[of p M X] dim_col_basis_mat[of p M X] assms  
    unfolding carrier_mat_def by auto
  show ?thesis
    using lower_triangular_imp_distinct[OF in_set] form_basis_is_lower_triangular no_zeros_on_diagonal assms rows_eq 
    by auto
qed

lemma det_of_matrix:
  fixes M X:: "nat"
  assumes "M > 0"
  assumes "X > 0"
  assumes "degree p  1"
  assumes d_is: "d = degree p"
  assumes n_is: "n = (id. i)"
  assumes monic_poly: "coeff p d = 1"
  shows "det (vec_list_to_square_mat (form_basis p M X (degree p - 1))) =
    M^(degree p)*X^n"
proof - 
  let ?A = "vec_list_to_square_mat (form_basis p M X (degree p - 1))"
  have in_set:"?A  carrier_mat (d+1) (d+1)"
    using d_is dim_row_basis_mat[of p M X] dim_col_basis_mat[of p M X] assms  
    unfolding carrier_mat_def 
    by auto
  then have "det ?A = prod_list (diag_mat ?A)"
    using det_lower_triangular no_zeros_on_diagonal_helper form_basis_is_lower_triangular
      assms 
    by metis
  have all_elems_diag: "i. i < d + 1  (?A $$ (i, i)) = ((form_basis p M X (degree p - 1)) ! i)$i"
    by (metis assms(3) assms(4) carrier_matD(2) in_set list_of_vec_index list_of_vec_row_nth matrix_row_form_basis)
  have last_elem: "((form_basis p M X (d - 1)) ! (d))$(d) = ((vec_associated_to_int_poly p X) $ (d))"
    using dim_form_basis_helper[of p M X "d-1"]  ith_row_form_basis(2)[of p M X "d-1"]  d_is
    by (metis Nat.le_imp_diff_is_add assms(3)) 
  have "(i<d+1. (((form_basis p M X (d - 1)) ! i)$i)) = (i<d. (((form_basis p M X (d - 1)) ! i)$i))*(((form_basis p M X (d - 1)) ! d)$d)"
    by auto
  then have prod_is: "(i<d+1. (((form_basis p M X (d - 1)) ! i)$i)) = (i<d. (((form_basis p M X (d - 1)) ! i)$i))*((vec_associated_to_int_poly p X) $ (d))"
    using last_elem by auto
  have "prod_list (diag_mat ?A) = (i<d+1. (?A $$ (i, i)))"
    unfolding diag_mat_def using dim_row_basis_mat[of p M X] assms(3) d_is
    by (metis (no_types, lifting) atLeast_upt distinct_upt prod.distinct_set_conv_list) 
  then have eq1: "prod_list (diag_mat ?A) = (i<d+1. (((form_basis p M X (d - 1)) ! i)$i))"
    using all_elems_diag d_is by auto
  have sub_prod_is: "(i<d. ((((form_basis p M X (d-1)))! i)$i)) = (i<d. ((((form_basis_helper p M X (d-1)))! i)$i))"
    using ith_row_form_basis(1) by auto
  have "prod_list (diag_mat ?A) = (i<d. ((((form_basis p M X (d-1)))! i)$i)) * ((vec_associated_to_int_poly p X) $ (d))"
    using  HOL.trans[OF eq1 prod_is] by auto
  then have prod_list_is: "prod_list (diag_mat ?A) = (i<d. ((((form_basis_helper p M X (d-1)))! i)$i)) * ((vec_associated_to_int_poly p X) $ (d))"
    using sub_prod_is by auto
  have prod_two: "(vec_associated_to_int_poly p X) $ (d) = X^d"
    unfolding vec_associated_to_poly_def 
    using d_is monic_poly by auto
  have same_prod: "(i<d. ((form_basis_helper p M X (d-1))! i)$i) =  (i<d. (g_i_vec M X i (degree p + 1))$i)"
    using ith_row_form_basis_helper by auto 
  have "k. k  degree p  (i<k. (g_i_vec M X i (degree p + 1))$i) = M^k*X^(i<k. i)"
  proof -
    fix k 
    assume "k  degree p"
    then show "(i<k. (g_i_vec M X i (degree p + 1))$i) = M^k*X^(i<k. i)"
    proof (induct k)
      case 0
      then show ?case unfolding g_i_vec_def by auto 
    next
      case (Suc d)
      have "(i<Suc d. g_i_vec M X i (degree p + 1) $ i) = (i<d. g_i_vec M X i (degree p + 1) $ i)*g_i_vec M X (d) (degree p + 1) $ (d)"
        by auto 
      then have h1: "(i<Suc d. g_i_vec M X i (degree p + 1) $ i) = (M ^ d * X ^  {..<d})*g_i_vec M X (d) (degree p + 1) $ (d)"
        using Suc by auto
      have h2: "g_i_vec M X (d) (degree p + 1) $ (d) = M*X^d "
        using Suc(2) unfolding g_i_vec_def by auto 
      show ?case
        using h1 h2 
        by (simp add: mult.left_commute power_add)
    qed
  qed
  then have "(i<d. (g_i_vec M X i (degree p + 1))$i) = M^d*X^(i<d. i)"
    using d_is by auto
  then have prod_one: "(i<d. ((form_basis_helper p M X (d-1))! i)$i) = M^d*X^(i<d. i)"
    using same_prod 
    by auto 
  have n_is_alt: "n = (i<d. i) + d"
    using n_is 
    by (metis lessThan_Suc_atMost sum.lessThan_Suc)
  have "prod_list (diag_mat ?A) = M^d*X^(i<d. i)*X^d"
    using prod_one prod_two prod_list_is by auto
  then have "prod_list (diag_mat ?A) = M^d*X^((i<d. i) + d)"
    by (simp add: power_add)
  then have det_prod: "prod_list (diag_mat ?A) = M^(degree p)*X^n"
    using n_is d_is n_is_alt by auto
  have "det ?A = prod_list (diag_mat ?A)"
    using prod_list_is form_basis_is_lower_triangular det_lower_triangular
    using det (vec_list_to_square_mat (form_basis p M X (degree p - 1))) = prod_list (diag_mat (vec_list_to_square_mat (form_basis p M X (degree p - 1)))) by force 
  then show ?thesis
    using det_prod by auto
qed

subsubsection ‹ Properties of casted matrix ›

lemma dim_row_basis_of_int_mat:
  assumes "degree p  1"
  shows "dim_row (vec_list_to_square_mat (map of_int_vec (form_basis p M X (degree p - 1)))) = degree p + 1"
proof -
  have "length (form_basis p M X (degree p - 1)) = degree p + 1"
    using assms dim_form_basis[of p M X "degree p - 1"] 
    by auto 
  then show ?thesis 
    by (simp add: vec_list_to_square_mat_def)
qed

lemma dim_col_basis_of_int_mat:
  assumes "degree p  1"
  shows"dim_col (vec_list_to_square_mat (map of_int_vec (form_basis p M X (degree p - 1)))) = (degree p + 1)"
proof - 
  have "length (form_basis p M X (degree p - 1)) = degree p + 1"
    using assms dim_form_basis[of p M X "degree p - 1"] 
    by auto 
  then show ?thesis unfolding vec_list_to_square_mat_def
    by auto
qed

lemma of_int_matrix_row_form_basis_carrier:
  assumes "degree p  1"
  assumes "i < degree p + 1"
  shows "(map of_int_vec (form_basis p M X (degree p - 1))) ! i  carrier_vec (degree p + 1)"
  using matrix_row_form_basis_carrier assms 
  by (metis (no_types, lifting) Suc_eq_plus1 add_Suc_right arith_special(3) dim_form_basis map_carrier_vec nth_map ordered_cancel_comm_monoid_diff_class.diff_add)

lemma of_int_matrix_carrier:
  assumes "degree p  1"
  assumes "i < degree p + 1"
  shows "vec_list_to_square_mat (map of_int_vec (form_basis p M X (degree p - 1)))  carrier_mat (degree p + 1) (degree p + 1)"
  using dim_row_basis_of_int_mat[of p M X] dim_col_basis_of_int_mat[of p M X] assms 
  by auto

lemma of_int_matrix_row_form_basis:
  assumes "degree p  1"
  assumes i_lt: "i < degree p + 1"
  shows "row (vec_list_to_square_mat (map of_int_vec (form_basis p M X (degree p - 1)))) i = (map of_int_vec (form_basis p M X (degree p - 1))) ! i"
proof - 
  let ?vs = "form_basis p M X (degree p - 1)"
  have cv: "map of_int_vec (form_basis p M X (degree p - 1)) ! i
     carrier_vec (degree p + 1)" using matrix_row_form_basis_carrier[of p i M X]
    using of_int_matrix_row_form_basis_carrier[of p i M X] assms  by auto
  have i_lt_len: "i < length (map of_int_vec (form_basis p M X (degree p - 1)))"
    using i_lt dim_form_basis[of p M X "degree p - 1"]
    by auto 
  show ?thesis
    unfolding vec_list_to_square_mat_def using mat_of_rows_row[OF i_lt_len cv]
      dim_form_basis[of p M X "degree p - 1"]
    using assms(1) by auto
qed

lemma of_int_matrix_row_form_basis_var:
  assumes "degree p  1"
  assumes i_lt: "i < degree p + 1"
  shows "row (vec_list_to_square_mat (map of_int_vec (form_basis p M X (degree p - 1)))) i = of_int_vec ((form_basis p M X (degree p - 1)) ! i)"
  using of_int_matrix_row_form_basis assms
  by (metis add_2_eq_Suc' dim_form_basis le_add_diff_inverse nth_map plus_1_eq_Suc semiring_norm(174)) 

lemma of_int_mat_element:
  fixes i j::"nat"
  assumes "degree p  1"
  assumes "i < (degree p + 1)"
  assumes "j < (degree p + 1)"
  shows "(vec_list_to_square_mat (map of_int_vec (form_basis p M X (degree p - 1)))) $$ (i,j) = 
    of_int ((vec_list_to_square_mat (form_basis p M X (degree p - 1))) $$ (i,j))"
proof -
  let ?M1 = "vec_list_to_square_mat (form_basis p M X (degree p - 1))"
  let ?M2 = "vec_list_to_square_mat (map of_int_vec (form_basis p M X (degree p - 1)))"
  have "?M1 $$ (i, j) = (row ?M1 i) $ j"
    using assms 
    by (metis dim_col_basis_mat dim_row_basis_mat index_row(1))
  then have row1: "?M1 $$ (i, j) = ((form_basis p M X (degree p - 1)) ! i)$j"
    using assms(1) assms(2) matrix_row_form_basis by presburger
  have  "?M2 $$ (i, j) = (row ?M2 i) $ j"
    using assms dim_col_basis_of_int_mat dim_row_basis_of_int_mat
    by (metis index_row(1))
  then have row2: "?M2 $$ (i, j) = (of_int_vec ((form_basis p M X (degree p - 1)) ! i)) $j"
    using assms of_int_matrix_row_form_basis_var 
    by metis
  show ?thesis
    using row1 row2
    by (metis assms(1) assms(2) assms(3) dim_col_basis_mat index_map_vec(1) index_row(2) matrix_row_form_basis) 
qed

lemma of_int_mat_is_lower_triangular:
  fixes i j::"nat"
  assumes "degree p  1"
  assumes "i < j"
  assumes "j < (degree p + 1)"
  shows "(vec_list_to_square_mat (map of_int_vec (form_basis p M X (degree p - 1)))) $$ (i,j) = 0"
  using assms of_int_mat_element form_basis_is_lower_triangular
  by (smt (verit, best) less_imp_le_nat nat_SN.compat of_int_code(2))

lemma of_int_mat_no_zeros_on_diagonal:
  assumes p_gt: "degree p  1"
  assumes "M > 0"
  assumes "X > 0"
  shows "0  set (diag_mat (vec_list_to_square_mat ((map of_int_vec (form_basis p M X (degree p - 1)))::rat vec list)))"
proof - 
  let ?M1 = "vec_list_to_square_mat (form_basis p M X (degree p - 1))"
  let ?M2 = "vec_list_to_square_mat ((map of_int_vec (form_basis p M X (degree p - 1)))::rat vec list)"
  let ?dim = "degree p + 1"
  have all: "i. i < ?dim  ?M2 $$ (i, i) = 0  False"
  proof - 
    fix i
    assume i_lt: "i < degree p + 1"
    assume M2_elt: "vec_list_to_square_mat
          ((map of_int_vec (form_basis p M X (degree p - 1)))::rat vec list) $$
         (i, i) = 0"
    obtain k::"int" where k_prop: "?M1 $$ (i, i) = k"
      using i_lt matrix_carrier[of p i]
      by auto
    then have k_inset: "k  set (map (λi. ?M1 $$ (i, i)) [0..<?dim])"
      using i_lt nth_map_upt[of i ?dim 0 "λi. ?M1 $$ (i, i)"] dim_row_basis_mat[of p M X] 
      by (metis (no_types, lifting) add.left_neutral diff_zero in_set_conv_nth length_map length_upt)
    have same_elt: "?M2 $$ (i, i) = rat_of_int (?M1 $$ (i, i))"
      using of_int_mat_element[OF p_gt i_lt i_lt] i_lt 
      by auto
    then have "(0::rat) = rat_of_int k"
      using M2_elt k_prop 
      by auto
    then have "k = 0"
      by auto
    then have "0  set (diag_mat ?M1)"
      using k_inset unfolding diag_mat_def
      using dim_row_basis_mat[of p M X] assms 
      by auto
    then show "False"
      using no_zeros_on_diagonal assms 
      by blast
  qed
  then have "0  set (map (λi. ?M2 $$(i, i)) [0..< ?dim])  False" 
  proof - 
    assume "0  set (map (λi. ?M2 $$(i, i)) [0..< ?dim])"
    then have "i  set [0..<degree p + 1]. 0 = ?M2$$(i, i)"
      using all atLeastLessThan_iff imageE list.set_map set_upt by force
    then have "i < degree p + 1. 0 = ?M2$$(i, i)"
      using atLeast_upt by blast
    then show "False" using all 
      by fastforce
  qed
  then have "0  set (diag_mat (vec_list_to_square_mat ((map of_int_vec (form_basis p M X (degree p - 1)))::rat vec list)))  False"
    using dim_row_basis_of_int_mat assms
    unfolding diag_mat_def 
    by (smt (verit, best))
  then show ?thesis by auto
qed

lemma of_int_mat_form_basis_distinct:
  assumes "degree p  1"
  assumes "M > 0"
  assumes "X > 0"
  shows "distinct ((map of_int_vec (form_basis p M X (degree p - 1)))::rat vec list) "
proof - 
  let ?M = "vec_list_to_square_mat ((map of_int_vec (form_basis p M X (degree p - 1)))::rat vec list)"
  let ?dim = "degree p + 1"
  have rows_eq: "rows ?M = ((map of_int_vec (form_basis p M X (degree p - 1)))::rat vec list)"
    unfolding vec_list_to_square_mat_def
    apply (intro rows_mat_of_rows)
    apply clarsimp
    by (smt (verit, ccfv_SIG) One_nat_def add.commute add_Suc_right assms(1) dim_form_basis in_set_conv_nth matrix_row_form_basis_carrier one_add_one ordered_cancel_comm_monoid_diff_class.diff_add plus_1_eq_Suc)
  have in_set:"?M  carrier_mat ?dim ?dim"                
    using dim_row_basis_of_int_mat[of p M X] dim_col_basis_of_int_mat[of p M X] assms  
    unfolding carrier_mat_def by auto
  show ?thesis
    using lower_triangular_imp_distinct[OF in_set] of_int_mat_is_lower_triangular of_int_mat_no_zeros_on_diagonal assms rows_eq 
    by metis
qed

lemma of_int_form_basis_lin_ind:
  assumes "M > 0"
  assumes "X > 0"
  assumes "degree p  1"
  shows "¬ module.lin_dep class_ring (module_vec TYPE(rat) (degree p + 1))
        (set ((map of_int_vec (form_basis p M X (degree p - 1)))::rat vec list))"
proof - 
  let ?M = "vec_list_to_square_mat ((map of_int_vec (form_basis p M X (degree p - 1)))::rat vec list)"
  have rows_M: "(rows
             (vec_list_to_square_mat
               (map of_int_vec (form_basis p M X (degree p - 1))))::rat vec list)
    = (map of_int_vec (form_basis p M X (degree p - 1))::rat vec list)"
    unfolding vec_list_to_square_mat_def 
    apply (intro rows_mat_of_rows)
    apply clarsimp
    apply (subst dim_form_basis)
    by (smt (verit) One_nat_def add.commute add_Suc_right assms(3) dim_form_basis in_set_conv_nth matrix_row_form_basis_carrier nat_1_add_1 ordered_cancel_comm_monoid_diff_class.diff_add plus_1_eq_Suc)
  have no_zeros_on_diagonal: " 0  set (diag_mat ?M)"
    using of_int_mat_no_zeros_on_diagonal assms 
    by auto
  have lower_triangular: "(i j. i < j  j < (degree p + 1)  ?M $$ (i, j) = 0) "
    using of_int_mat_is_lower_triangular assms by auto
  have form_basis_distinct: "distinct ((map of_int_vec (form_basis p M X (degree p - 1))::rat vec list))"
    using of_int_mat_form_basis_distinct assms by auto
  have matrix_carrier: "?M  carrier_mat (degree p + 1) (degree p + 1)"
    using of_int_matrix_carrier assms by auto 
  have "¬ module.lin_dep class_ring (module_vec TYPE(rat) (degree p + 1)) (set (rows ?M))"
    using idom_vec.lower_triangular_imp_lin_indpt_rows[OF matrix_carrier lower_triangular]
      form_basis_distinct no_zeros_on_diagonal
    by blast
  then show ?thesis using rows_M 
    by auto
qed 

subsection ‹ Top-level proof ›

lemma towards_coppersmith:
  fixes p f:: "int poly"
  fixes M X:: "nat"
  fixes x0:: "int"
  assumes zero_mod_M: "poly p x0 mod M = 0"
  assumes d_is: "d = degree p" 
  assumes d_gt: "d > 0"
  assumes monic_poly: "coeff p d = 1"
  assumes X_gt: "X > 0"  
  assumes X_lt: "X < 1/(sqrt 2)*1/(root d (d+1))*(root (d*(d+1)) M)^2"
  assumes M_gt: "M > 0"
  assumes x0_le: "abs x0  X"
  assumes f_is: "f = towards_coppersmith p M X"
  shows "poly f x0 = 0"
proof -
  let ?cs_basis = "form_basis p M X (degree p - 1)"

  have li1_1: " vec_associated_to_int_poly p (int X)  carrier_vec (Suc d)"
    by (metis assms(2) carrier_vec_dim_vec dim_vec_vec_associated_to_poly semiring_norm(174))
  have li1_2: "xa. xa  set (form_basis_helper p M X (degree p - Suc 0))  xa  carrier_vec (Suc d)"
    by (metis (no_types, lifting) assms(2) carrier_vec_dim_vec dim_form_basis_helper dim_vec g_i_vec_def in_set_conv_nth ith_row_form_basis_helper le_simps(2) semiring_norm(174))
  have li1: "set (map of_int_vec ?cs_basis)  carrier_vec (d + 1)"
    unfolding form_basis_def using li1_1 li1_2
    by auto

  have "distinct ?cs_basis"
    using d_is d_gt X_gt M_gt form_basis_distinct by force
  then have li2: "distinct ((map of_int_vec ?cs_basis)::rat vec list)"
    using casted_distinct_is_distinct by blast

  have li3:" module.lin_indpt class_ring
        (module_vec TYPE(rat) (d + 1))
        (set (map of_int_vec
               (form_basis p M X
                 (degree p - 1))))"
    unfolding form_basis_def
    using M_gt d_is X_gt d_gt form_basis_def of_int_form_basis_lin_ind by auto

  have 2: "a. a  set ?cs_basis  poly (int_poly_associated_to_vec a X) x0 mod M = 0" 
  proof - 
    fix a
    assume "a  set ?cs_basis"
    then obtain i where a_prop: "i < d + 1  a = ?cs_basis ! i"
      by (metis (no_types, lifting) Suc_leI add_Suc_right arith_special(3) d_is d_gt dim_form_basis in_set_conv_nth numeral_nat(7) ordered_cancel_comm_monoid_diff_class.diff_add semiring_norm(174))
    {assume *: "i = d"
      then have " poly (int_poly_associated_to_vec a X) x0 mod M = 0"
        using  zero_mod_M
        using ith_row_form_basis a_prop *
        by (metis One_nat_def Suc_leI vec_associated_to_int_poly_inverse X_gt d_is d_gt ordered_cancel_comm_monoid_diff_class.diff_add)
    } moreover {assume *: "i < d"
      then have a_is:"a = g_i_vec M X i (d + 1)"
        using ith_row_form_basis d_is a_prop ith_row_form_basis_helper 
        by simp
      have i_lteq: "i  degree p"
        using d_is * by auto
      have "int_poly_associated_to_vec a X = monom (int M) i"
        using d_is a_is int_poly_associated_to_g_i_vec[OF X_gt M_gt i_lteq] 
        by blast
      then have "poly (int_poly_associated_to_vec a X) x0 = x0^i * (int M)"
        by (simp add: poly_monom)
      then have "poly (int_poly_associated_to_vec a X) x0 mod M = 0"
        by auto
    }
    ultimately show "poly (int_poly_associated_to_vec a X) x0 mod M = 0" 
      using a_prop 
      by (metis Suc_eq_plus1 Suc_leI linorder_neqE_nat not_less)
  qed
  let ?vec = "vec_associated_to_int_poly p X"
  have vaip_div:"j. j < dim_vec ?vec  ?vec $ j mod X ^ j = 0"
    by (simp add: vec_associated_to_poly_def)
  have form_basis_helper_div:"(v j. v  set (form_basis_helper p M X (degree p - 1)) 
            j < dim_vec v  v $ j mod X ^ j = 0)" 
  proof - 
    fix v j
    assume v_inset: "v  set (form_basis_helper p M X (degree p - 1))"
    assume j_lt: "j < dim_vec v"
    obtain i where "i < degree p  v = (form_basis_helper p M X (degree p - 1)) ! i"
      using v_inset dim_form_basis_helper 
      by (metis (no_types, lifting) add.right_neutral add_Suc_right add_diff_inverse_nat assms(2) d_gt in_set_conv_nth less_Suc_eq not_less_zero plus_1_eq_Suc)
    then have v_is: "v = g_i_vec M X i (degree p + 1)"
      using ith_row_form_basis_helper[of i "degree p - 1" p M X]  
      by (metis Suc_pred' assms(2) d_gt le_simps(2))
    {assume *: "j = i"
      then have "v$j = int M * int X ^ i"
        using v_is j_lt unfolding g_i_vec_def 
        by fastforce
      then have "v $ j mod X ^ j = 0"
        using * by auto 
    } moreover {assume *: "j  i"
      then have "v$j = 0"
        using v_is j_lt unfolding g_i_vec_def 
        by auto
      then have "v $ j mod X ^ j = 0" 
        by auto
    }
    ultimately show "v $ j mod X ^ j = 0"
      by blast
  qed

  then have 1: "(v j. v  set ?cs_basis 
            j < d + 1  v $ j mod X ^ j = 0)"
    using vaip_div form_basis_helper_div ith_row_form_basis set_form_basis
    by (metis assms(2) dim_form_basis dim_vector_in_basis in_set_conv_nth) 

  have " det (vec_list_to_square_mat (?cs_basis)) =
    int (M ^ degree p * X ^  {..d})"
    apply (intro det_of_matrix[OF M_gt X_gt _ d_is _ monic_poly])
    using d_is d_gt by auto
  
  then have d: "det (mat_of_rows (d + 1) ?cs_basis) =  M^d * X^(d * (d+1) div 2)"
    unfolding vec_list_to_square_mat_def
      dim_form_basis
    by (metis Suc_pred' add_2_eq_Suc' d_is d_gt atLeast0AtMost gauss_sum_nat semiring_norm(174))

  have d1: "(sqrt 2)^(d* (d+1)) = 2 ^ (d * (d + 1) div 2)"
    apply (subst sqrt_even_pow2[symmetric])
    using real_sqrt_power by auto

  have d2:"root d (real (d + 1)) ^ (d * (d + 1)) = (d + 1) ^ (d + 1)"
    by (metis d_gt of_nat_0_le_iff power_mult real_root_pow_pos2 semiring_1_class.of_nat_power)
    
  have "sqrt 2 * root d (d+1) * X < (root (d*(d+1)) M)^2"
    using X_lt d_gt
    by (auto simp add: field_simps)

  then have "(sqrt 2 * root d (d+1) * X) ^ (d * (d+1)) < M ^ 2"
    by (smt (verit, ccfv_SIG) Num.of_nat_simps(4) d_gt linordered_semiring_strict_class.mult_pos_pos mult_sign_intros(1) nat_less_real_le of_nat_0_le_iff real_root_ge_0_iff real_root_less_iff real_root_pos_unique real_root_power real_sqrt_ge_0_iff semiring_1_class.of_nat_power)

  moreover have "(sqrt 2 * root d (d+1) * X) ^ (d * (d+1)) =
    (sqrt 2)^(d* (d+1))  * X ^ (d * (d+1)) * 
    (root d (d+1))^(d * (d+1))"
    by (auto simp add: field_simps)

  ultimately have "(2 ^ (d * (d + 1) div 2) *
     real (X ^ (d * (d + 1) div 2))^2 *
    real ((d + 1) ^ (d + 1)))
    < M ^ 2 "
    unfolding d1 d2
    by (smt (verit) Num.of_nat_simps(2) Num.of_nat_simps(4) Num.of_nat_simps(5) d1 d2 dvd_div_mult_self even_add even_mult_iff more_arith_simps(6) of_nat_le_iff one_add_one power_mult semiring_1_class.of_nat_power)
  then have "
     (2 ^ (d * (d + 1) div 2) *
     (X ^ (d * (d + 1) div 2))^2 *
    ((d + 1) ^ (d + 1)))
    < M ^ 2 "
    by (smt (verit) Num.of_nat_simps(2) Num.of_nat_simps(4) Num.of_nat_simps(5) nat_1_add_1 of_nat_power_less_of_nat_cancel_iff semiring_1_class.of_nat_power)
  
  then have "
     (2 ^ (d * (d + 1) div 2) *
     (X ^ (d * (d + 1) div 2))^2 *
    ((d + 1) ^ (d + 1)))  * M ^ (2 * d)
    < M ^ 2  * M ^ (2 * d)"
    by (simp add: M_gt)
 
  then have h3: "2 ^ (d * (d + 1) div 2) *
    (M ^ d *  X ^ (d * (d + 1) div 2))2 *
    (d + 1) ^ (d + 1)
    < (M ^ (2 * (d + 1)))"
    by (auto simp add: algebra_simps monoid_mult_class.power_mult power2_eq_square)
  then have "Suc d ^ d * ((M ^ d)2 * (2 ^ ((d + d * d) div 2) * (X ^ ((d + d * d) div 2))2)) +
    d * (Suc d ^ d * ((M ^ d)2 * (2 ^ ((d + d * d) div 2) * (X ^ ((d + d * d) div 2))2)))
    < M * (M * M ^ (d * 2)) 
    (1 + real d) ^ d *
    ((real M ^ d)2 * (2 ^ ((d + d * d) div 2) * (real X ^ ((d + d * d) div 2))2)) +
    real d *
    ((1 + real d) ^ d *
     ((real M ^ d)2 * (2 ^ ((d + d * d) div 2) * (real X ^ ((d + d * d) div 2))2)))
    < real M * (real M * real M ^ (d * 2))"
    proof -
    assume a1: "Suc d ^ d * ((M ^ d)2 * (2 ^ ((d + d * d) div 2) * (X ^ ((d + d * d) div 2))2)) + d * (Suc d ^ d * ((M ^ d)2 * (2 ^ ((d + d * d) div 2) * (X ^ ((d + d * d) div 2))2))) < M * (M * M ^ (d * 2))"
    have "n na. real n < real na  ¬ n < na"
      by simp
    then have "(1 + real d) ^ d * real ((M ^ d)2 * (2 ^ ((d + d * d) div 2) * (X ^ ((d + d * d) div 2))2)) + real d * ((1 + real d) ^ d * real ((M ^ d)2 * (2 ^ ((d + d * d) div 2) * (X ^ ((d + d * d) div 2))2))) < real (M * (M * M ^ (d * 2)))"
      using a1 by (smt (z3) Num.of_nat_simps(2) Num.of_nat_simps(4) Num.of_nat_simps(5) plus_1_eq_Suc semiring_1_class.of_nat_power)
    then show "(1 + real d) ^ d * ((real M ^ d)2 * (2 ^ ((d + d * d) div 2) * (real X ^ ((d + d * d) div 2))2)) + real d * ((1 + real d) ^ d * ((real M ^ d)2 * (2 ^ ((d + d * d) div 2) * (real X ^ ((d + d * d) div 2))2))) < real M * (real M * real M ^ (d * 2))"
      by simp
  qed
  then have 3: "real_of_rat 2 ^
    ((d + 1) * (d + 1 - 1) div 2) *
    real_of_int
     ((det (mat_of_rows (d + 1)
             (form_basis p M X
               (degree p -
                1))))2) *
    (real (d + 1)) ^ (d + 1)
    < real (M ^ (2 * (d + 1)))"
    using h3
    unfolding d
    by (auto simp add: field_simps)
    
  interpret lll: LLL_with_assms
    "d+1" "d+1" "?cs_basis" "2"
    apply unfold_locales
    subgoal by auto
    subgoal
      unfolding cof_vec_space.lin_indpt_list_def
      using li1 li2 li3 by auto
    using assms(2) d_gt dim_form_basis by auto
  
  interpret cs: coppersmith_assms
    "d+1" "d+1" "?cs_basis"
    "2" x0 M X p
    apply unfold_locales
    subgoal using M_gt by auto
    subgoal using X_gt by auto
    subgoal by auto
    subgoal using zero_mod_M by auto
    subgoal using x0_le by auto
    subgoal
      using assms(2) d_gt dim_form_basis by auto
    subgoal using 1 by auto
    using 2 by auto

  have *: "lll.short_vector = short_vector 2 ?cs_basis"
    using lll.short_vector_impl by presburger

  from cs.root_poly_short_vector
  show ?thesis
    using cs.bnd_raw_imp_short_vec_bound 3 lll.square_Gramian_determinant_eq_det_square
    unfolding f_is towards_coppersmith_def first_vector_def Let_def *
    by auto
qed

lemma towards_coppersmith_pretty:
  fixes p f:: "int poly"
  fixes M X:: "nat"
  fixes x0:: "int"
  defines "d  degree p"
  defines "f  towards_coppersmith p M X"
  assumes monic_poly: "monic p"
  assumes "d > 0" and "M > 0" and "X > 0"
  assumes zero_mod_M: "poly p x0 mod M = 0"
  assumes X_lt: "X < 1/(sqrt 2)*1/(root d (d+1))*(root (d*(d+1)) M)^2"
  assumes x0_le: "abs x0  X"
  shows "poly f x0 = 0"
  using assms towards_coppersmith
  by presburger

end