Theory Degree

theory Degree
  imports Notation
begin

subsection ‹Degree of a given variable›

lemma degree_Const [simp]: "degree (Const x) v = 0"
  unfolding degree_def Const_def Const0_def
  by (simp add: MPoly_inverse)

lemma degree_Var [simp]:
  "degree ((Var v):: 'a::comm_semiring_1 mpoly) v' = (if v = v' then 1 else 0)"
  unfolding degree_def Var_def Var0_def
  by (simp add: MPoly_inverse lookup_single)

lemma degree_neg:
  fixes P :: "'a::ab_group_add mpoly"
  shows "degree (- P) = degree P"
  unfolding degree.rep_eq uminus_mpoly.rep_eq keys.rep_eq
  by auto

lemma degree_add:
  fixes P Q :: "'a::ab_group_add mpoly"
  shows "degree (P + Q) v  max (degree P v) (degree Q v)"
proof - 
  { fix m assume "m  keys (mapping_of (P + Q))" 
    hence "m  keys (mapping_of P)  m  keys (mapping_of Q)"
      by (metis add.right_neutral coeff_add coeff_keys)
    moreover have "m  keys (mapping_of P)  lookup m v  degree P v"
      by (simp add: degree_def)
    moreover have "m  keys (mapping_of Q)  lookup m v  degree Q v"
      by (simp add: degree_def)
    ultimately have "lookup m v  max (degree P v) (degree Q v)"
      by auto }
  thus ?thesis by (simp add: degree_def)
qed

lemma degree_add':
  fixes P Q :: "'a::ab_group_add mpoly"
  shows "degree (P + Q) v  degree P v + degree Q v"
  using degree_add
  by (metis max_def trans_le_add1 trans_le_add2)

lemma degree_add_different_degree:
  fixes P :: "'a::ab_group_add mpoly"
  assumes "degree P v  degree Q v"
  shows "degree (P + Q) v = max (degree P v) (degree Q v)"
proof -
  have 0: "Max (insert 0 ((λm. lookup m v) ` keys (mapping_of P))) 
           Max (insert 0 ((λm. lookup m v) ` keys (mapping_of Q)))"
    using assms unfolding degree.rep_eq .
  have "Max (insert 0 ((λm. lookup m v) ` keys (mapping_of P + mapping_of Q))) =
        max (Max (insert 0 ((λm. lookup m v) ` keys (mapping_of P))))
            (Max (insert 0 ((λm. lookup m v) ` keys (mapping_of Q))))"
  proof cases
    assume 1: "(λm. lookup m v) ` keys (mapping_of P) = {}"
    show ?thesis
    proof cases
      assume 2: "(λm. lookup m v) ` keys (mapping_of Q) = {}"
      show ?thesis using 0 1 assms unfolding degree.rep_eq by simp
    next
      assume 3: "(λm. lookup m v) ` keys (mapping_of Q)  {}"
      have "(λm. lookup m v) ` keys (mapping_of P + mapping_of Q) =
            (λm. lookup m v) ` keys (mapping_of Q)"
        using 1 3 by auto
      thus ?thesis using 1 by auto
    qed
  next
    assume 4: "(λm. lookup m v) ` keys (mapping_of P)  {}"
    show ?thesis
    proof cases
      assume 5: "(λm. lookup m v) ` keys (mapping_of Q) = {}"
      have "(λm. lookup m v) ` keys (mapping_of P + mapping_of Q) =
            (λm. lookup m v) ` keys (mapping_of P)"
        using 4 5 by auto
      thus ?thesis using 5 by auto
    next
      assume 6: "(λm. lookup m v) ` keys (mapping_of Q)  {}"
      have 7: "(λm. lookup m v) ` keys (mapping_of P + mapping_of Q)  {}"
        using 0 eq_neg_iff_add_eq_0 by force
      obtain a where 8: "lookup a v = (MAX mkeys (mapping_of P). lookup m v)"
                        "lookup (mapping_of P) a  0"
                        "m. lookup (mapping_of P) m  0  lookup m v  lookup a v"
        using 4
        by (smt (verit, best) Max_ge Max_in finite_imageI finite_keys image_iff in_keys_iff)
      obtain b where 9: "lookup b v = (MAX mkeys (mapping_of Q). lookup m v)"
                        "lookup (mapping_of Q) b  0"
                        "m. lookup (mapping_of Q) m  0  lookup m v  lookup b v"
        using 6
        by (smt (verit, best) Max_ge Max_in finite_imageI finite_keys image_iff in_keys_iff)
      obtain c where 10: "lookup c v = (MAX mkeys (mapping_of P + mapping_of Q). lookup m v)"
                         "lookup (mapping_of P) c + lookup (mapping_of Q) c  0"
                         "m. lookup (mapping_of P) m + lookup (mapping_of Q) m  0 
                           lookup m v  lookup c v"
        using 7
        by (smt (verit, del_insts) Max_ge Max_in finite_imageI finite_keys image_iff 
            lookup_not_eq_zero_eq_in_keys plus_poly_mapping.rep_eq)
      have 11: "lookup a v  lookup b v" unfolding 8(1) 9(1) using 0 4 6 by simp
      have "lookup c v = max (lookup a v) (lookup b v)"
        using 8 9 10 11
        by (smt (verit, del_insts) add_cancel_right_right max.bounded_iff order_class.order_eq_iff)
      hence "(MAX mkeys (mapping_of P + mapping_of Q). lookup m v) =
             max (MAX mkeys (mapping_of P). lookup m v)
                 (MAX mkeys (mapping_of Q). lookup m v)"
        unfolding 8(1) 9(1) 10(1) .
      thus ?thesis using 4 6 7 by auto
    qed
  qed
  thus ?thesis unfolding degree.rep_eq plus_mpoly.rep_eq plus_poly_mapping.rep_eq .
qed

lemma degree_diff:
  fixes P Q :: "'a::ab_group_add mpoly"
  shows "degree (P - Q) v  max (degree P v) (degree Q v)"
  unfolding diff_conv_add_uminus[of P] degree_neg[of Q, symmetric]
  by (rule degree_add)

lemma degree_diff':
  fixes P Q :: "'a::ab_group_add mpoly"
  shows "degree (P - Q) v  degree P v + degree Q v"
  unfolding diff_conv_add_uminus[of P] degree_neg[of Q, symmetric]
  by (rule degree_add')

lemma degree_diff_different_degree:
  fixes P :: "'a::ab_group_add mpoly"
  assumes "degree P v  degree Q v"
  shows "degree (P - Q) v = max (degree P v) (degree Q v)"
  unfolding diff_conv_add_uminus[of P] degree_neg[of Q, symmetric]
  apply (rule degree_add_different_degree)
  unfolding degree_neg apply (rule assms)
  done

lemma degree_sum:
  fixes P :: "'a  'b::ab_group_add mpoly"
  assumes S_fin: "finite S"
  shows "degree (sum P S) v  Max (insert 0 ((λi. degree (P i) v) ` S))"
proof (induct rule:finite_induct[OF S_fin])
  case 1 show ?case by simp
next 
  case (2 x S)
  have "degree (sum P (insert x S)) v = degree (sum P S + P x) v"
    by (simp add: "2.hyps" add.commute)
  also have "...  max (degree (sum P S) v) (degree (P x) v)"
    using degree_add by auto
  also have "...  max (Max (insert 0 ((λi. degree (P i) v) ` S))) (degree (P x) v)"
    using "2.hyps" by auto
  also have "... = Max (insert (degree (P x) v) (insert 0 ((λi. degree (P i) v) ` S)))"
    by (simp add: "2.hyps")
  also have "... = Max (insert 0 ((λi. degree (P i) v) ` (insert x S)))"
    by (simp add: insert_commute)
  finally show ?case .
qed

lemma degree_mult: "degree (P * Q) v  degree P v + degree Q v"
proof -
  have "m  keys (mapping_of (P * Q)) 
        lookup m v  degree P v + degree Q v" for m
  proof -
    assume "m  keys (mapping_of (P * Q))"
    hence "m  keys (mapping_of P * mapping_of Q)"
      by (simp add: times_mpoly.rep_eq)
    hence "m  {a + b | a b. a  keys (mapping_of P)  b  keys (mapping_of Q)}"
      using keys_mult by blast
    then obtain a b where m_def: "m = a + b"
                      and a_key: "a  keys (mapping_of P)"
                      and b_key: "b  keys (mapping_of Q)"
      by blast
    from a_key have a_bound: "lookup a v  degree P v"
      unfolding degree.rep_eq by simp
    from b_key have b_bound: "lookup b v  degree Q v"
      unfolding degree.rep_eq by simp
    have "lookup m v = lookup a v + lookup b v"
      unfolding m_def lookup_add by simp
    thus "lookup m v  degree P v + degree Q v"
      using a_bound b_bound by simp
  qed
  thus ?thesis unfolding degree.rep_eq by simp
qed

lemma degree_mult_non_zero:
  fixes P Q :: "'a::idom mpoly"
  assumes "P  0" "Q  0"
  shows "degree (P * Q) v = degree P v + degree Q v"
proof (rule antisym)
  show "degree (P * Q) v  degree P v + degree Q v" by (rule degree_mult)
next
  define a where "a = Max ((λm. lookup m v) ` keys (mapping_of P))"
  define b where "b = Max ((λm. lookup m v) ` keys (mapping_of Q))"
  define c where "c = Max ((λm. lookup m v) ` keys (mapping_of (P * Q)))"
  define p where "p = Max {m  keys (mapping_of P). lookup m v = a}"
  define q where "q = Max {m  keys (mapping_of Q). lookup m v = b}"
  define r where "r = Max {m  keys (mapping_of (P * Q)). lookup m v = c}"
  have 0: "P * Q  0" using assms by auto
  have 1: "keys (mapping_of P)  {}"
          "keys (mapping_of Q)  {}"
          "keys (mapping_of (P * Q))  {}"
    using assms 0 mapping_of_inject zero_mpoly.rep_eq
    by (metis keys_eq_empty)+
  have 2: "a  (λm. lookup m v) ` keys (mapping_of P)"
          "b  (λm. lookup m v) ` keys (mapping_of Q)"
          "c  (λm. lookup m v) ` keys (mapping_of (P * Q))"
    unfolding a_def b_def c_def
    using 1 Max_in
    by simp+
  have 3: "p  {m  keys (mapping_of P). lookup m v = a}"
          "q  {m  keys (mapping_of Q). lookup m v = b}"
          "r  {m  keys (mapping_of (P * Q)). lookup m v = c}"
    unfolding p_def q_def r_def
    using 2 Max_in[of "{m  keys (mapping_of _). lookup m v = _}"]
    by auto
  have 4: "m. m  keys (mapping_of P)  lookup m v  a"
          "m. m  keys (mapping_of Q)  lookup m v  b"
          "m. m  keys (mapping_of (P * Q))  lookup m v  c"
    unfolding a_def b_def c_def
    by auto
  have 5: "m. m  keys (mapping_of P)  lookup m v = a  m  p"
          "m. m  keys (mapping_of Q)  lookup m v = b  m  q"
          "m. m  keys (mapping_of (P * Q))  lookup m v = c  m  r"
    unfolding p_def q_def r_def
    by auto
  have "p' + q' = p + q 
        lookup (mapping_of P) p'  0 
        lookup (mapping_of Q) q'  0 
        p' = p  q' = q" for p' q'
  proof -
    assume 6: "p' + q' = p + q"
    assume 7: "lookup (mapping_of P) p'  0" "lookup (mapping_of Q) q'  0"
    have 8: "lookup p' v  lookup p v" "lookup q' v  lookup q v"
      using 3 4 7 unfolding in_keys_iff by auto
    have "lookup p' v + lookup q' v = lookup p v + lookup q v"
      using 6
      by (metis lookup_add)
    hence "lookup p' v = lookup p v  lookup q' v = lookup q v"
      using 8
      by linarith
    hence "p'  p" "q'  q"
      using 3 5 7 unfolding in_keys_iff by blast+
    thus "p' = p  q' = q"
      using 6
      by (metis add.commute add_le_cancel_left antisym)
  qed
  hence 9: "(p + q = p' + q' 
             lookup (mapping_of P) p'  0 
             lookup (mapping_of Q) q'  0) 
            (p', q') = (p, q)" for p' q'
    using 3 by auto
  have "lookup (mapping_of (P * Q)) (p + q) =
        ((p', q'). lookup (mapping_of P) p' * lookup (mapping_of Q) q' when p + q = p' + q')"
    unfolding times_mpoly.rep_eq times_poly_mapping.rep_eq
    by (rule prod_fun_unfold_prod; simp)
  also have "... = ((p', q'). lookup (mapping_of P) p' * lookup (mapping_of Q) q' 
              when p + q = p' + q'  lookup (mapping_of P) p'  0  lookup (mapping_of Q) q'  0)"
    apply (rule Sum_any.cong)
    unfolding when_def apply auto
    done
  also have "... = Sum_any (λa. (case a of (p', q') 
                     lookup (mapping_of P) p' * lookup (mapping_of Q) q') when a = (p, q))"
    unfolding 9
    by (rule Sum_any.cong; auto)
  also have "... = lookup (mapping_of P) p * lookup (mapping_of Q) q"
    unfolding Sum_any_when_equal
    by auto
  also have "...  0"
    using 3 by auto
  finally have 10: "lookup (mapping_of (P * Q)) (p + q)  0" .
  have "a + b = lookup (p + q) v"
    using 3
    unfolding plus_poly_mapping.rep_eq by simp
  also have "...  c"
    by (rule 4(3)[unfolded in_keys_iff]; rule 10)
  finally show "degree P v + degree Q v  degree (P * Q) v"
    unfolding a_def b_def c_def degree.rep_eq
    using 1
    by simp
qed

lemma degree_pow: "degree (P ^ n) v  n * degree P v"
proof (induct n)
  case 0
  show ?case by simp
next
  case (Suc n)
  have "degree (P ^ Suc n) v  degree P v + degree (P ^ n) v"
    using degree_mult by auto
  also have "...  degree P v + n * degree P v"
    using Suc by auto
  also have "... = Suc n * degree P v"
    by auto
  finally show ?case .
qed

lemma degree_pow_positive:
  fixes P :: "'a::idom mpoly"
  assumes "n > 0"
  shows "degree (P ^ n) v = n * degree P v"
proof (induction rule: nat_induct_non_zero[OF assms])
  show "degree (P ^ 1) v = 1 * degree P v" by simp
next
  fix m :: nat
  assume 0: "m > 0"
  assume 1: "degree (P ^ m) v = m * degree P v"
  show "degree (P ^ Suc m) v = Suc m * degree P v"
  proof cases
    assume 2: "P = 0"
    show ?thesis unfolding 2 by simp
  next
    assume 3: "P  0"
    show ?thesis
      unfolding power_Suc2
      apply (subst degree_mult_non_zero)
      subgoal using 0 3 pow_positive by blast
      subgoal using 3 .
      subgoal using 1 by simp
      done
  qed
qed

lemma degree_prod:
  assumes S_fin: "finite S"
  shows "degree (prod P S) v  sum (λi. degree (P i) v) S"
proof (induct rule:finite_induct[OF S_fin])
  case 1 show ?case by simp
next
  case (2 x S)
  have "degree (prod P (insert x S)) v = degree (prod P S * P x) v"
    by (simp add: "2.hyps" mult.commute)
  also have "...  (degree (prod P S) v) + (degree (P x) v)"
    using degree_mult by auto
  also have "...  (sum (λi. degree (P i) v) S) + (degree (P x) v)"
    using "2.hyps" by auto
  also have "... = sum (λi. degree (P i) v) (insert x S)"
    by (simp add: "2.hyps")
  finally show ?case .
qed


end