Session Padic_Ints

Theory Function_Ring

theory Function_Ring
  imports "HOL-Algebra.Ring" "HOL-Library.FuncSet" "HOL-Algebra.Module"
begin

text‹
  This theory formalizes basic facts about the ring of extensional functions from a fixed set to
  a fixed ring. This will be useful for providing a generic framework for various constructions
  related to the $p$-adics such as polynomial evaluation and sequences. The rings of semialgebraic
  functions will be defined as subrings of these function rings, which will be necessary for the
  proof of $p$-adic quantifier elimination.
›

(**************************************************************************************************)
(**************************************************************************************************)
section‹The Ring of Extensional Functions from a Fixed Base Set to a Fixed Base Ring›
(**************************************************************************************************)
(**************************************************************************************************)

  (**************************************************************************************************)
  (**************************************************************************************************)
  subsection‹Basic Operations on Extensional Functions›
  (**************************************************************************************************)
  (**************************************************************************************************)

definition function_mult:: "'c set  ('a, 'b) ring_scheme  ('c  'a)  ('c  'a)  ('c  'a)" where
"function_mult S R f g = (λx  S. (f x) R (g x))"

abbreviation(input) ring_function_mult:: "('a, 'b) ring_scheme  ('a  'a)  ('a  'a)  ('a  'a)" where
"ring_function_mult R f g  function_mult (carrier R) R f g"

definition function_add:: "'c set  ('a, 'b) ring_scheme  ('c  'a)  ('c  'a)  ('c  'a)" where
"function_add S R f g = (λx  S. (f x) R (g x))"

abbreviation(input) ring_function_add:: "('a, 'b) ring_scheme  ('a  'a)  ('a  'a)  ('a  'a)" where
"ring_function_add R f g  function_add (carrier R) R f g"

definition function_one:: "'c set  ('a, 'b) ring_scheme  ('c  'a)" where
"function_one S R = (λx  S. 𝟭R)"

abbreviation(input) ring_function_one :: "('a, 'b) ring_scheme  ('a  'a)" where
"ring_function_one R  function_one (carrier R) R"

definition function_zero:: "'c set  ('a, 'b) ring_scheme  ('c  'a)" where
"function_zero S R = (λx  S. 𝟬R)"

abbreviation(input) ring_function_zero :: "('a, 'b) ring_scheme  ('a  'a)" where
"ring_function_zero R  function_zero (carrier R) R"

definition function_uminus:: "'c set  ('a, 'b) ring_scheme  ('c  'a)  ('c  'a)" where
"function_uminus S R a = (λ x  S. R (a x))"

definition ring_function_uminus:: " ('a, 'b) ring_scheme  ('a  'a)  ('a  'a)" where
"ring_function_uminus R a = function_uminus (carrier R) R a"

definition function_scalar_mult:: "'c set  ('a, 'b) ring_scheme  'a  ('c  'a)  ('c  'a)" where
"function_scalar_mult S R a f = (λ x  S. a R (f x))"

  (**************************************************************************************************)
  (**************************************************************************************************)
  subsection‹Defining the Ring of Extensional Functions›
  (**************************************************************************************************)
  (**************************************************************************************************)

definition function_ring:: "'c set  ('a, 'b) ring_scheme  ( 'a, 'c  'a) module" where
"function_ring S R = 
   carrier = extensional_funcset S (carrier R),
   Group.monoid.mult = (function_mult S R),
   one = (function_one S R),
   zero = (function_zero S R),
   add = (function_add S R),
   smult = function_scalar_mult S R  "

text‹The following locale consists of a struct R, and a distinguished set S which is meant to serve as the domain for a ring of functions $S \to carrier R$. ›
locale struct_functions = 
  fixes R ::"('a, 'b) partial_object_scheme"  (structure) 
    and S :: "'c set" 

text‹The following are locales which fix a ring R (which may be commutative, a domain, or a field) and a function ring F of extensional functions from a fixed set S to $carrier R$›
locale ring_functions  = struct_functions + R?: ring R +
  fixes F (structure)
  defines F_def: "F  function_ring S R"

locale cring_functions = ring_functions + R?: cring R

locale domain_functions = ring_functions + R?: domain R

locale field_functions = ring_functions + R?: field R

sublocale cring_functions < ring_functions 
  apply (simp add: ring_functions_axioms)
  by (simp add: F_def)
  
sublocale domain_functions < ring_functions 
  apply (simp add: ring_functions_axioms)
  by (simp add: F_def)
  
sublocale domain_functions < cring_functions 
  apply (simp add: cring_functions_def is_cring ring_functions_axioms)
  by (simp add: F_def)

sublocale field_functions < domain_functions 
  apply (simp add: domain_axioms domain_functions_def ring_functions_axioms)
  by (simp add: F_def) 
    
sublocale field_functions < ring_functions
  apply (simp add: ring_functions_axioms)
  by (simp add: F_def) 

sublocale field_functions < cring_functions
  apply (simp add: cring_functions_axioms)
  by (simp add: F_def)

abbreviation(input) ring_function_ring:: "('a, 'b) ring_scheme  ('a, 'a  'a) module" ("Fun") where
"ring_function_ring R  function_ring (carrier R) R"


  (**************************************************************************************************)
  (**************************************************************************************************)
  subsection‹Algebraic Properties of the Basic Operations›
  (**************************************************************************************************)
  (**************************************************************************************************)

    (**************************************************************************************************)
    (**************************************************************************************************)
    subsubsection‹Basic Carrier Facts›
    (**************************************************************************************************)
    (**************************************************************************************************)

lemma(in ring_functions) function_ring_defs:
"carrier F = extensional_funcset S (carrier R)"
"(⊗F) = (function_mult S R)"
"(⊕F) = (function_add S R)"
"𝟭F = function_one S R"
"𝟬F = function_zero S R"
"(⊙F) = function_scalar_mult S R"
  unfolding F_def 
  by ( auto simp add: function_ring_def)

lemma(in ring_functions) function_ring_car_memE:
  assumes "a  carrier F"
  shows "a  extensional S"
        "a  S  carrier R"
  using assms function_ring_defs apply auto[1]
  using assms function_ring_defs  PiE_iff apply blast
  using assms  function_ring_defs(1) by fastforce
  
lemma(in ring_functions) function_ring_car_closed:
  assumes "a  S"
  assumes "f  carrier F"
  shows "f a  carrier R"
  using assms   unfolding function_ring_def F_def by auto 
  
lemma(in ring_functions)  function_ring_not_car:
  assumes "a  S"
  assumes "f  carrier F"
  shows "f a = undefined"
    using assms   unfolding function_ring_def F_def by auto 
    
lemma(in ring_functions)  function_ring_car_eqI:
  assumes "f  carrier F"
  assumes "g  carrier F"
  assumes "a. a  S  f a = g a"
  shows "f = g"
  using assms(1) assms(2) assms(3) extensionalityI function_ring_car_memE(1) by blast

lemma(in ring_functions) function_ring_car_memI:
  assumes "a. a  S  f a  carrier R"
  assumes " a. a  S f a = undefined"
  shows "f  carrier F"
  using function_ring_defs assms 
  unfolding extensional_funcset_def 
  by (simp add: a. a  S  f a  carrier R extensional_def)

lemma(in ring) function_ring_car_memI:
  assumes "a. a  S  f a  carrier R"
  assumes " a. a  S f a = undefined"
  shows "f  carrier (function_ring S R)"
 by (simp add: assms(1) assms(2) local.ring_axioms ring_functions.function_ring_car_memI ring_functions.intro)

    (**************************************************************************************************)
    (**************************************************************************************************)
    subsubsection‹Basic Multiplication Facts›
    (**************************************************************************************************)
    (**************************************************************************************************)

lemma(in ring_functions) function_mult_eval_car:
  assumes "a  S"
  assumes "f  carrier F"
  assumes "g  carrier F"
  shows "(f F g) a = (f a)  (g a)"
  using assms function_ring_defs 
  unfolding function_mult_def 
  by simp

lemma(in ring_functions) function_mult_eval_closed:
  assumes "a  S"
  assumes "f  carrier F"
  assumes "g  carrier F"
  shows "(f F g) a  carrier R"
  using assms function_mult_eval_car
  using F_def ring_functions.function_ring_car_closed ring_functions_axioms by fastforce
  
lemma(in ring_functions) fun_mult_closed:
  assumes "f  carrier F"
  assumes "g  carrier F"
  shows "f F g  carrier F"
  apply(rule function_ring_car_memI)
  apply (simp add: assms(1) assms(2) function_mult_eval_closed)
  by (simp add: function_mult_def function_ring_defs(2))

lemma(in ring_functions) fun_mult_eval_assoc:
  assumes "x  carrier F"
  assumes "y  carrier F" 
  assumes " z  carrier F"
  assumes "a  S"
  shows "(x F y F z) a = (x F (y F z)) a"
proof-
  have 0: "(x F y F z) a = (x a)  (y a)  (z a) "
    by (simp add: assms(1) assms(2) assms(3) assms(4) fun_mult_closed function_mult_eval_car)
  have 1: "(x F (y F z)) a = (x a)  ((y a)  (z a))"
    by (simp add: assms(1) assms(2) assms(3) assms(4) fun_mult_closed function_mult_eval_car)
  have 2:"(x F (y F z)) a = (x a)  (y a)  (z a)"
    using 1 assms 
    by (simp add: function_ring_car_closed m_assoc)    
  show ?thesis 
    using 0 2 by auto 
qed

lemma(in ring_functions) fun_mult_assoc:
  assumes "x  carrier F"
  assumes "y  carrier F" 
  assumes "z  carrier F"
  shows "(x F y F z) = (x F (y F z))"
  using fun_mult_eval_assoc[of x]
  by (simp add: assms(1) assms(2) assms(3) fun_mult_closed function_ring_car_eqI)
    (**************************************************************************************************)
    (**************************************************************************************************)
    subsubsection‹Basic Addition Facts›
    (**************************************************************************************************)
    (**************************************************************************************************)

lemma(in ring_functions) fun_add_eval_car:
  assumes "a  S"
  assumes "f  carrier F"
  assumes "g  carrier F"
  shows "(f F g) a = (f a)  (g a)"
  by (simp add: assms(1) function_add_def function_ring_defs(3))

lemma(in ring_functions) fun_add_eval_closed:
  assumes "a  S"
  assumes "f  carrier F"
  assumes "g  carrier F"
  shows "(f F g) a  carrier R"
  using assms unfolding F_def 
  using F_def fun_add_eval_car function_ring_car_closed 
  by auto

lemma(in ring_functions) fun_add_closed:
  assumes "f  carrier F"
  assumes "g  carrier F"
  shows "f F g  carrier F"
  apply(rule function_ring_car_memI)
  using assms unfolding F_def
  using F_def fun_add_eval_closed apply blast
  by (simp add: function_add_def function_ring_def)

lemma(in ring_functions) fun_add_eval_assoc:
  assumes "x  carrier F"
  assumes "y  carrier F" 
  assumes " z  carrier F"
  assumes "a  S"
  shows "(x F y F z) a = (x F (y F z)) a"
proof-
  have 0: "(x F y F z) a = (x a)  (y a)  (z a) "
    by (simp add: assms(1) assms(2) assms(3) assms(4) fun_add_closed fun_add_eval_car)
  have 1: "(x F (y F z)) a = (x a)  ((y a)  (z a))"
    by (simp add: assms(1) assms(2) assms(3) assms(4) fun_add_closed fun_add_eval_car)
  have 2:"(x F (y F z)) a = (x a)  (y a)  (z a)"
    using 1 assms 
    by (simp add: add.m_assoc function_ring_car_closed)   
  show ?thesis 
    using 0 2 by auto 
qed

lemma(in ring_functions) fun_add_assoc:
  assumes "x  carrier F"
  assumes "y  carrier F" 
  assumes " z  carrier F"
  shows "x F y F z = x F (y F z)"
  apply(rule function_ring_car_eqI)
  using assms apply (simp add: fun_add_closed)
   apply (simp add: assms(1) assms(2) assms(3) fun_add_closed)
     by (simp add: assms(1) assms(2) assms(3) fun_add_eval_assoc)
  
lemma(in ring_functions) fun_add_eval_comm:
  assumes "a  S"
  assumes "x  carrier F"
  assumes "y  carrier F" 
  shows "(x F y) a = (y F x) a"
  by (metis F_def assms(1) assms(2) assms(3) fun_add_eval_car ring.ring_simprules(10) ring_functions.function_ring_car_closed ring_functions_axioms ring_functions_def)
    
lemma(in ring_functions) fun_add_comm:
  assumes "x  carrier F"
  assumes "y  carrier F" 
  shows "x F y = y F x"
  using fun_add_eval_comm assms 
  by (metis (no_types, hide_lams) fun_add_closed function_ring_car_eqI)

    (**************************************************************************************************)
    (**************************************************************************************************)
    subsubsection‹Basic Facts About the Multiplicative Unit›
    (**************************************************************************************************)
    (**************************************************************************************************)

lemma(in ring_functions) function_one_eval:
  assumes "a  S"
  shows "𝟭F a = 𝟭"
  using assms function_ring_defs unfolding function_one_def  
  by simp
  
lemma(in ring_functions) function_one_closed:
"𝟭F carrier F"
  apply(rule function_ring_car_memI)
  using function_ring_defs 
  using function_one_eval apply auto[1]
  by (simp add: function_one_def function_ring_defs(4))

lemma(in ring_functions) function_times_one_l:
  assumes "a  carrier F"
  shows "𝟭F F a = a"
proof(rule function_ring_car_eqI)
  show "𝟭F F a  carrier F"
    using assms fun_mult_closed function_one_closed 
    by blast
  show " a  carrier F"
    using assms by simp 
  show "c. c  S  (𝟭F F a) c = a c "
    by (simp add: assms function_mult_eval_car function_one_eval function_one_closed function_ring_car_closed)
qed

lemma(in ring_functions) function_times_one_r:
  assumes "a  carrier F"
  shows "aF 𝟭F  = a"
proof(rule function_ring_car_eqI)
  show "aF 𝟭F  carrier F"
    using assms fun_mult_closed function_one_closed 
    by blast
  show " a  carrier F"
    using assms by simp 
  show "c. c  S  (aF 𝟭F) c = a c "
    using assms 
    by (simp add: function_mult_eval_car function_one_eval function_one_closed function_ring_car_closed)
qed

    (**************************************************************************************************)
    (**************************************************************************************************)
    subsubsection‹Basic Facts About the Additive Unit›
    (**************************************************************************************************)
    (**************************************************************************************************)

lemma(in ring_functions) function_zero_eval:
  assumes "a  S"
  shows "𝟬F a = 𝟬"
  using assms function_ring_defs 
  unfolding function_zero_def
  by simp
  
lemma(in ring_functions) function_zero_closed:
"𝟬F carrier F"
  apply(rule function_ring_car_memI)
  apply (simp add: function_zero_eval)
  by (simp add: function_ring_defs(5) function_zero_def)

lemma(in ring_functions) fun_add_zeroL:
  assumes "a  carrier F"
  shows "𝟬F F a = a"
proof(rule function_ring_car_eqI)
  show "𝟬F F a  carrier F"
    using assms fun_add_closed function_zero_closed 
    by blast
  show "a  carrier F"
    using assms by simp 
  show "c. c  S  (𝟬F F a) c = a c "
    using assms F_def fun_add_eval_car function_zero_closed 
      ring_functions.function_zero_eval ring_functions_axioms 
    by (simp add: ring_functions.function_zero_eval function_ring_car_closed)
qed

lemma(in ring_functions) fun_add_zeroR:
  assumes "a  carrier F"
  shows "a F 𝟬F = a"
  using assms fun_add_comm fun_add_zeroL 
  by (simp add: function_zero_closed)

    (**************************************************************************************************)
    (**************************************************************************************************)
    subsubsection‹Distributive Laws›
    (**************************************************************************************************)
    (**************************************************************************************************)

lemma(in ring_functions) function_mult_r_distr: 
  assumes "x  carrier F"
  assumes" y  carrier F"
  assumes " z  carrier F"
  shows " (x F y) F z = x F z F y F z"
proof(rule function_ring_car_eqI)
  show "(x F y) F z  carrier F"
    by (simp add: assms(1) assms(2) assms(3) fun_add_closed fun_mult_closed)      
  show "x F z F y F z  carrier F"
    by (simp add: assms(1) assms(2) assms(3) fun_add_closed fun_mult_closed)   
  show  "a. a  S  ((x F y) F z) a = (x F z F y F z) a"
  proof-
    fix a
    assume A: "a  S"
    show "((x F y) F z) a = (x F z F y F z) a"
      using A assms fun_add_eval_car[of a x y]  fun_add_eval_car[of a "x Fz" "y F z"] 
            function_mult_eval_car[of a "x F y" z] semiring_simprules(10) 
            F_def 
      by (smt fun_add_closed function_mult_eval_car function_ring_car_closed 
          ring_functions.fun_mult_closed ring_functions_axioms)            
  qed
qed    

lemma(in ring_functions) function_mult_l_distr:
  assumes "x  carrier F"
  assumes" y  carrier F"
  assumes " z  carrier F"
  shows "z F (x F y) = z F x F z F y"
proof(rule function_ring_car_eqI)
  show "z F (x F y)  carrier F"
    by (simp add: assms(1) assms(2) assms(3) fun_add_closed fun_mult_closed)     
  show "z F x F z F y  carrier F"
    by (simp add: assms(1) assms(2) assms(3) fun_add_closed fun_mult_closed)   
  show  "a. a  S  (z F (x F y)) a = (z F x F z F y) a"
  proof-
    fix a
    assume A: "a  S"
    show "(z F (x F y)) a = (z F x F z F y) a"
      using A assms function_ring_defs fun_add_closed fun_mult_closed 
            function_mult_eval_car[of a z "x F y"] 
            function_mult_eval_car[of a z x] 
            function_mult_eval_car[of a z y] 
            fun_add_eval_car[of a x y]
            semiring_simprules(13) 
            fun_add_eval_car function_ring_car_closed by auto  
  qed
qed    

    (**************************************************************************************************)
    (**************************************************************************************************)
    subsubsection‹Additive Inverses›
    (**************************************************************************************************)
    (**************************************************************************************************)

lemma(in ring_functions) function_uminus_closed:
  assumes "f  carrier F"
  shows "function_uminus S R f  carrier F"
proof(rule function_ring_car_memI)
  show "a. a  S  function_uminus S R f a  carrier R"
    using assms function_ring_car_closed[of _ f] unfolding function_uminus_def 
    by simp       
  show "a. a  S  function_uminus S R f a = undefined"
    by (simp add: function_uminus_def)
qed

lemma(in ring_functions) function_uminus_eval:
  assumes "a  S"
  assumes "f  carrier F"
  shows "(function_uminus S R f) a =  (f a)"
  using assms unfolding function_uminus_def 
  by simp

lemma(in ring_functions) function_uminus_add_r:
  assumes "a  S"
  assumes "f  carrier F"
  shows "f F function_uminus S R f = 𝟬F"
  apply(rule function_ring_car_eqI) 
  using assms  fun_add_closed function_uminus_closed apply blast
    unfolding F_def  using F_def function_zero_closed apply blast
      using F_def assms(2) fun_add_eval_car function_ring_car_closed function_uminus_closed 
      function_uminus_eval function_zero_eval r_neg by auto

lemma(in ring_functions) function_uminus_add_l:
  assumes "a  S"
  assumes "f  carrier F"
  shows "function_uminus S R f F f = 𝟬F"
  using assms(1) assms(2) fun_add_comm function_uminus_add_r function_uminus_closed by auto
  

    (**************************************************************************************************)
    (**************************************************************************************************)
    subsubsection‹Scalar Multiplication›
    (**************************************************************************************************)
    (**************************************************************************************************)

lemma(in ring_functions) function_smult_eval:
  assumes "a  carrier R"
  assumes  "f  carrier F"
  assumes "b  S"
  shows "(a F f) b = a  (f b)"
  using function_ring_defs(6) unfolding function_scalar_mult_def 
  by(simp add: assms)

lemma(in ring_functions) function_smult_closed:
  assumes "a  carrier R"
  assumes  "f  carrier F"
  shows "a F f  carrier F"
  apply(rule function_ring_car_memI)
  using function_smult_eval assms 
  apply (simp add: function_ring_car_closed)
  using function_scalar_mult_def F_def 
  by (metis function_ring_defs(6) restrict_apply)

lemma(in ring_functions) function_smult_assoc1:
  assumes "a  carrier R"
  assumes "b  carrier R"
  assumes  "f  carrier F"
  shows "b F (a F f)  = (b  a)Ff"
  apply(rule function_ring_car_eqI)
  using assms function_smult_closed apply simp
    using assms function_smult_closed apply simp
       by (metis F_def assms(1) assms(2) assms(3) function_mult_eval_closed function_one_closed
        function_smult_eval function_times_one_r m_assoc m_closed ring_functions.function_smult_closed ring_functions_axioms)

lemma(in ring_functions) function_smult_assoc2:
  assumes "a  carrier R"
  assumes  "f  carrier F"
  assumes "g  carrier F"
  shows "(a F f)Fg  = a F (f F g)"
  apply(rule function_ring_car_eqI)
  using assms function_smult_closed apply (simp add: fun_mult_closed)
   apply (simp add: assms(1) assms(2) assms(3) fun_mult_closed function_smult_closed)
     by (metis (full_types) F_def assms(1) assms(2) assms(3) fun_mult_closed 
      function_mult_eval_car function_smult_closed function_smult_eval m_assoc ring_functions.function_ring_car_closed ring_functions_axioms)     

lemma(in ring_functions) function_smult_one:
  assumes  "f  carrier F"
  shows "𝟭Ff = f"
  apply(rule function_ring_car_eqI)
  apply (simp add: assms function_smult_closed)
   apply (simp add: assms)
     by (simp add: assms function_ring_car_closed function_smult_eval)
     
lemma(in ring_functions) function_smult_l_distr:
"[| a  carrier R; b  carrier R; x  carrier F |] ==>
      (a  b) F x = a F x F b F x"
  apply(rule function_ring_car_eqI)
  apply (simp add: function_smult_closed)
   apply (simp add: fun_add_closed function_smult_closed)   
    using function_smult_eval 
    by (simp add: fun_add_eval_car function_ring_car_closed function_smult_closed l_distr)
    
lemma(in ring_functions) function_smult_r_distr:
 "[| a  carrier R; x  carrier F; y  carrier F |] ==>
      a F (x F y) = a F x F a F y"
  apply(rule function_ring_car_eqI)
  apply (simp add: fun_add_closed function_smult_closed)
   apply (simp add: fun_add_closed function_smult_closed)
    by (simp add: fun_add_closed fun_add_eval_car function_ring_car_closed function_smult_closed function_smult_eval r_distr) 

    (**************************************************************************************************)
    (**************************************************************************************************)
    subsubsection‹The Ring of Functions Forms an Algebra›
    (**************************************************************************************************)
    (**************************************************************************************************)

lemma(in ring_functions) function_ring_is_abelian_group:
"abelian_group F"
  apply(rule abelian_groupI)
  apply (simp add: fun_add_closed)
      apply (simp add: function_zero_closed)
        using fun_add_assoc apply simp  
          apply (simp add: fun_add_comm)
            apply (simp add: fun_add_comm fun_add_zeroR function_zero_closed)
              using fun_add_zeroL function_ring_car_eqI function_uminus_add_l 
                  function_uminus_closed function_zero_closed by blast

lemma(in ring_functions) function_ring_is_monoid:
"monoid F"
  apply(rule monoidI)
    apply (simp add: fun_mult_closed)
     apply (simp add: function_one_closed)
      apply (simp add: fun_mult_assoc)
       apply (simp add: function_times_one_l)
        by (simp add: function_times_one_r)
      
lemma(in ring_functions) function_ring_is_ring:
"ring F"
  apply(rule ringI)
     apply (simp add: function_ring_is_abelian_group)
      apply (simp add: function_ring_is_monoid)
        apply (simp add: function_mult_r_distr)
          by (simp add: function_mult_l_distr)

sublocale ring_functions < F?: ring F
  by (rule function_ring_is_ring)

lemma(in cring_functions) function_mult_comm: 
  assumes "x  carrier F"
  assumes" y  carrier F"
  shows "x F y = y F x"
  apply(rule function_ring_car_eqI)
  apply (simp add: assms(1) assms(2) fun_mult_closed)
   apply (simp add: assms(1) assms(2) fun_mult_closed)
    by (simp add: assms(1) assms(2) function_mult_eval_car function_ring_car_closed m_comm)  

lemma(in cring_functions) function_ring_is_comm_monoid:
"comm_monoid F"
  apply(rule comm_monoidI)
  using fun_mult_assoc function_one_closed
  apply (simp add: fun_mult_closed)
     apply (simp add: function_one_closed)
      apply (simp add: fun_mult_assoc)
        apply (simp add: function_times_one_l)
          by (simp add: function_mult_comm)    
    
lemma(in cring_functions) function_ring_is_cring:
"cring F"
  apply(rule cringI)
    apply (simp add: function_ring_is_abelian_group)
      apply (simp add: function_ring_is_comm_monoid)
        by (simp add: function_mult_r_distr)

lemma(in cring_functions) function_ring_is_algebra:
"algebra R F"
  apply(rule algebraI)
   apply (simp add: is_cring)
    apply (simp add: function_ring_is_cring)
     using function_smult_closed apply blast
      apply (simp add: function_smult_l_distr)
       apply (simp add: function_smult_r_distr)
        apply (simp add: function_smult_assoc1)
         apply (simp add: function_smult_one)
          by (simp add: function_smult_assoc2)
            
lemma(in ring_functions) function_uminus:
  assumes "f  carrier F"
  shows "F f = (function_uminus S R) f"
  using assms a_inv_def[of F] 
  by (metis F_def abelian_group.a_group abelian_group.r_neg function_uminus_add_r function_uminus_closed group.inv_closed partial_object.select_convs(1) ring.ring_simprules(18) ring_functions.function_ring_car_eqI ring_functions.function_ring_is_abelian_group ring_functions.function_ring_is_ring ring_functions_axioms)

lemma(in ring_functions) function_uminus_eval':
  assumes "f  carrier F"
  assumes "a  S"
  shows "(F f) a = (function_uminus S R) f a"
  using assms 
  by (simp add: function_uminus)

lemma(in ring_functions) function_uminus_eval'':
  assumes "f  carrier F"
  assumes "a  S"
  shows "(F f) a =  (f a)"
  using assms(1) assms(2) function_uminus 
  by (simp add: function_uminus_eval)

sublocale cring_functions < F?: algebra R F
  using function_ring_is_algebra by auto 

    (**************************************************************************************************)
    (**************************************************************************************************)
    subsection‹Constant Functions›
    (**************************************************************************************************)
    (**************************************************************************************************)

definition constant_function  where
"constant_function S a =(λx  S. a)"

abbreviation(in ring_functions)(input) const   where
"const  constant_function S"

lemma(in ring_functions) constant_function_closed:
  assumes "a  carrier R"
  shows "const a  carrier F"
  apply(rule function_ring_car_memI)
  unfolding constant_function_def 
  apply (simp add: assms)
    by simp

lemma(in ring_functions) constant_functionE: 
  assumes "a  carrier R"
  assumes "b  S"
  shows "const a b = a"
  by (simp add: assms(2) constant_function_def)

lemma(in ring_functions) constant_function_add: 
  assumes "a  carrier R"
  assumes "b  carrier R"
  shows "const (a R b) = (const a) F (const b) " 
  apply(rule function_ring_car_eqI)
    apply (simp add: constant_function_closed assms(1) assms(2))
      using assms(1) constant_function_closed assms(2) fun_add_closed apply auto[1]
        by (simp add: assms(1) assms(2) constant_function_closed constant_functionE fun_add_eval_car)

lemma(in ring_functions) constant_function_mult: 
  assumes "a  carrier R"
  assumes "b  carrier R"
  shows "const (a R b) = (const a) F (const b)" 
  apply(rule function_ring_car_eqI)
    apply (simp add: constant_function_closed assms(1) assms(2))
      using assms(1) constant_function_closed assms(2) fun_mult_closed apply auto[1]
        by (simp add: constant_function_closed assms(1) assms(2) constant_functionE function_mult_eval_car)
      
lemma(in ring_functions) constant_function_minus: 
  assumes "a  carrier R"
  shows "F(const a) = (const (R a)) " 
apply(rule function_ring_car_eqI)
  apply (simp add: constant_function_closed assms local.function_uminus)
   apply (simp add: constant_function_closed assms function_uminus_closed)
    apply (simp add: constant_function_closed assms)
      by (simp add: constant_function_closed assms constant_functionE function_uminus_eval'')

lemma(in ring_functions) function_one_is_constant:
"const 𝟭 = 𝟭F"
  unfolding F_def 
  apply(rule function_ring_car_eqI)
  apply (simp add: constant_function_closed)
  using F_def function_one_closed apply auto[1]
  using F_def constant_functionE function_one_eval by auto

lemma(in ring_functions) function_zero_is_constant:
"const 𝟬 = 𝟬F"
   apply(rule function_ring_car_eqI)
  apply (simp add: constant_function_closed)
  using F_def function_zero_closed apply auto[1]
  using F_def constant_functionE function_zero_eval by auto


  (**************************************************************************************************)
  (**************************************************************************************************)
  subsection‹Special Examples of Functions Rings›
  (**************************************************************************************************)
  (**************************************************************************************************)

    (**************************************************************************************************)
    (**************************************************************************************************)
    subsubsection‹Functions from the Carrier of a Ring to Itself›
    (**************************************************************************************************)
    (**************************************************************************************************)


locale U_function_ring = ring

locale U_function_cring = U_function_ring + cring

sublocale U_function_ring <  S?: struct_functions R "carrier R" 
  done 

sublocale U_function_ring  <  FunR?: ring_functions R "carrier R" "Fun R"
  apply (simp add: local.ring_axioms ring_functions.intro)
    by simp

sublocale U_function_cring <  FunR?: cring_functions R "carrier R" "Fun R"
  apply (simp add: cring_functions_def is_cring ring_functions_axioms)
    by simp
    
abbreviation(in U_function_ring)(input) ring_compose :: "('a  'a)  ('a  'a)  ('a  'a)" where
"ring_compose  compose (carrier R)"
  
lemma(in U_function_ring) ring_function_ring_comp:
  assumes "f  carrier (Fun R)"
  assumes "g  carrier (Fun R)"
  shows "ring_compose f g  carrier (Fun R)"
  apply(rule function_ring_car_memI) 
  apply (simp add: assms(1) assms(2) compose_eq)
   apply (simp add: assms(1) assms(2) function_ring_car_closed)
     by (meson compose_extensional extensional_arb)
  
abbreviation(in U_function_ring)(input) ring_const ("𝔠ı") where
"ring_const  constant_function (carrier R)"

lemma(in ring_functions) function_nat_pow_eval:
  assumes "f  carrier F"
  assumes "s  S"
  shows "(f[^]F(n::nat)) s = (f s)[^]n"
  apply(induction n)
  using assms(2) function_one_eval apply auto[1]
  by (simp add: assms(1) assms(2) function_mult_eval_car function_ring_is_monoid monoid.nat_pow_closed)
    

context U_function_ring 
begin

definition a_translate :: "'a  'a  'a" where
"a_translate = (λ r  carrier R. restrict ((add R) r) (carrier R))"

definition m_translate :: "'a  'a  'a" where
"m_translate  = (λ r  carrier R. restrict ((mult R) r) (carrier R))"

definition nat_power :: "nat  'a  'a" where 
"nat_power = (λ(n::nat). restrict (λa.  a[^]Rn) (carrier R)) "

text‹Restricted operations are in Fs›

lemma a_translate_functions:
  assumes "c  carrier R"
  shows "a_translate c  carrier (Fun R)"
  apply(rule function_ring_car_memI)
  using assms a_translate_def 
   apply simp
  using assms a_translate_def 
  by simp  

lemma m_translate_functions:
  assumes "c  carrier R"
  shows "m_translate c  carrier (Fun R)"
  apply(rule function_ring_car_memI)
  using assms m_translate_def 
  apply simp
    using assms m_translate_def 
  by simp

lemma nat_power_functions:
  shows "nat_power n  carrier (Fun R)"
  apply(rule function_ring_car_memI)
  using  nat_power_def 
   apply simp
  by (simp add: nat_power_def)

text‹Restricted operations simps›

lemma a_translate_eq:
  assumes "c  carrier R"
  assumes "a  carrier R"
  shows "a_translate c a = c  a"
  by (simp add: a_translate_def assms(1) assms(2))

lemma a_translate_eq':
  assumes "c  carrier R"
  assumes "a  carrier R"
  shows "a_translate c a = undefined"
  by (meson a_translate_functions assms(1) assms(2) function_ring_not_car)

lemma a_translate_eq'':
  assumes "c  carrier R"
  shows "a_translate c = undefined"
  by (simp add: a_translate_def assms)

lemma m_translate_eq:
  assumes "c  carrier R"
  assumes "a  carrier R"
  shows "m_translate c a = c  a"
  by (simp add: m_translate_def assms(1) assms(2))

lemma m_translate_eq':
  assumes "c  carrier R"
  assumes "a  carrier R"
  shows "m_translate c a = undefined "
  by (meson m_translate_functions assms(1) assms(2) function_ring_not_car)

lemma m_translate_eq'':
  assumes "c  carrier R"
  shows "m_translate c = undefined"
  by (simp add: m_translate_def assms)

lemma nat_power_eq:
  assumes "a  carrier R"
  shows "nat_power n a = a[^]R n"
  by (simp add: assms nat_power_def)

lemma nat_power_eq':
  assumes "a  carrier R"
  shows "nat_power n a = undefined"
  by (simp add: assms nat_power_def)

text‹Constant ring\_function properties›

lemma constant_function_eq:
  assumes "a  carrier R"
  assumes "b  carrier R"
  shows "𝔠a b = a"
  using assms 
    
  by (simp add: constant_functionE)
    
lemma constant_function_eq':
  assumes "a  carrier R"
  assumes "b  carrier R"
  shows "𝔠a b = undefined"
  by (simp add: constant_function_closed assms(1) assms(2) function_ring_not_car)
    
text‹Compound expressions from algebraic operations›
end 

definition monomial_function where
"monomial_function R c (n::nat) = (λ x  carrier R. c R (x[^]Rn))"

context U_function_ring
begin

abbreviation monomial where
"monomial  monomial_function R"

lemma monomial_functions:
  assumes "c  carrier R"
  shows "monomial c n  carrier (Fun R)"
  apply(rule function_ring_car_memI)
  unfolding monomial_function_def 
  apply (simp add: assms)
  by simp

definition ring_id  where
"ring_id  restrict (λx. x) (carrier R) "

lemma ring_id_closed[simp]:
"ring_id  carrier (Fun R)"
  by (simp add: function_ring_car_memI ring_id_def)

lemma ring_id_eval:
  assumes "a  carrier R"
  shows "ring_id a = a"
  using assms unfolding ring_id_def
  by simp
  
lemma constant_a_trans: 
  assumes "a carrier R"
  shows "m_translate a  = 𝔠a Fun R ring_id"
proof(rule function_ring_car_eqI)
   show "m_translate a  carrier (Fun R)"
     using assms
     using m_translate_functions by blast
   show "𝔠a Fun R ring_id  carrier (Fun R)"
     unfolding ring_id_def 
     using assms ring_id_closed ring_id_def 
     by (simp add: constant_function_closed fun_mult_closed)      
  show "x. x  carrier R  m_translate a x = (𝔠a Fun R ring_id) x"
    by (simp add: constant_function_closed assms constant_function_eq function_mult_eval_car m_translate_eq ring_id_eval)    
qed

text‹polynomials in one variable›

fun polynomial :: "'a list  ('a  'a)" where
"polynomial []  = 𝟬Fun R "|
"polynomial (a#as) = (λx  carrier R. a  x  (polynomial as x))"

lemma polynomial_induct_lemma:
  assumes "f  carrier (Fun R)"
  assumes "a  carrier R"
  shows "(λx  carrier R. a  x  (f x))  carrier (Fun R)"
proof(rule function_ring_car_memI)
  show "aa. aa  carrier R  (λxcarrier R. a  x  f x) aa  carrier R"
  proof- fix y assume A: "y  carrier R"
    have "a  y  f y  carrier R"
      using A assms(1) assms(2) function_ring_car_closed by blast
    thus "(λxcarrier R. a  x  f x) y  carrier R"
      using A by auto 
  qed  
  show "aa. aa  carrier R  (λxcarrier R. a  x  f x) aa = undefined"
    by auto 
qed

lemma polynomial_function: 
  shows "set as  carrier R  polynomial as  carrier (Fun R)"
proof(induction as)
  case Nil
  then show ?case 
    by (simp add: function_zero_closed)  
next
  case (Cons a as)
  then show "polynomial (a # as)  carrier (function_ring (carrier R) R)"
    using polynomial.simps(2)[of a as] polynomial_induct_lemma[of "polynomial as" a]
    by simp
qed
  
lemma polynomial_constant:
  assumes "a  carrier R"
  shows "polynomial [a] = 𝔠a"
  apply(rule function_ring_car_eqI)
      using assms polynomial_function 
      apply (metis (full_types) list.distinct(1) list.set_cases set_ConsD subset_code(1))
        apply (simp add: constant_function_closed assms)
          using polynomial.simps(2)[of a "[]"] polynomial.simps(1) assms 
          by (simp add: constant_function_eq function_zero_eval)
          

end

    (**************************************************************************************************)
    (**************************************************************************************************)
    subsubsection‹Sequences Indexed by the Natural Numbers›
    (**************************************************************************************************)
    (**************************************************************************************************)

definition nat_seqs ("_ω")where
"nat_seqs R  function_ring (UNIV::nat set) R"
 
abbreviation(input) closed_seqs where
"closed_seqs R  carrier (Rω)"

lemma closed_seqs_memI:
  assumes "k. s k  carrier R"
  shows "s  closed_seqs R"
  unfolding nat_seqs_def function_ring_def 
  by (simp add: PiE_UNIV_domain assms)

lemma closed_seqs_memE:
  assumes "s  closed_seqs R"
  shows "s k  carrier R"
  using assms unfolding nat_seqs_def function_ring_def 
  by (simp add: PiE_iff)  

definition is_constant_fun  where
"is_constant_fun R f = (x  carrier R. f = constant_function (carrier R) R x)"

definition is_constant_seq where
"is_constant_seq R s = (x  carrier R. s = constant_function (UNIV::nat set) x)"

lemma is_constant_seqI:
  fixes a
  assumes "s  closed_seqs R"
  assumes "k. s k = a"
  shows "is_constant_seq R s"
  unfolding is_constant_seq_def constant_function_def 
  by (metis assms(1) assms(2) closed_seqs_memE restrict_UNIV restrict_ext)
   
lemma is_constant_seqE:
  assumes "is_constant_seq R s"
  assumes "s k = a"
  shows "s n = a"
  using assms unfolding is_constant_seq_def 
  by (metis constant_function_def restrict_UNIV)
  
lemma is_constant_seq_imp_closed:
  assumes "is_constant_seq R s"
  shows "s  closed_seqs R"
  apply(rule closed_seqs_memI)
  using assms unfolding is_constant_seq_def constant_function_def 
  by auto
  
context U_function_ring
begin

text‹Sequence sums and products are closed›

lemma seq_plus_closed:
  assumes "s  closed_seqs R"
  assumes "s'  closed_seqs R"
  shows "s Rω s'  closed_seqs R"
  by (metis assms(1) assms(2) nat_seqs_def ring_functions.fun_add_closed ring_functions_axioms)
 
lemma seq_mult_closed:
  assumes "s  closed_seqs R"
  assumes "s'  closed_seqs R"
  shows "s Rω s'  closed_seqs R"
  apply(rule closed_seqs_memI)
  by (metis assms(1) assms(2) closed_seqs_memE nat_seqs_def ring_functions.fun_mult_closed ring_functions_axioms)
 
lemma constant_function_comp_is_closed_seq:
  assumes "a  carrier R"
  assumes "s  closed_seqs R"
  shows "(const a  s)  closed_seqs R" 
  by (simp add: constant_functionE assms(1) assms(2) closed_seqs_memE closed_seqs_memI)

lemma constant_function_comp_is_constant_seq:
  assumes "a  carrier R"
  assumes "s  closed_seqs R"
  shows "is_constant_seq R ((const a)  s)" 
  apply(rule is_constant_seqI[of _ _ a] )
  apply (simp add: assms(1) assms(2) constant_function_comp_is_closed_seq)
    using assms(1) assms(2) closed_seqs_memE 
    by (simp add: closed_seqs_memE constant_functionE)
      
lemma function_comp_is_closed_seq:
  assumes "s  closed_seqs R"
  assumes "f  carrier (Fun R)"
  shows "f  s  closed_seqs R" 
  apply(rule closed_seqs_memI)
  using assms(1) assms(2) closed_seqs_memE 
  by (metis comp_apply fun_add_eval_closed fun_add_zeroR function_zero_closed)
  
lemma function_sum_comp_is_seq_sum:
  assumes "s  closed_seqs R"
  assumes "f  carrier (Fun R)"
  assumes "g  carrier (Fun R)"
  shows "(f Fun R g)  s = (f  s) Rω (g  s)"
  apply(rule ring_functions.function_ring_car_eqI[of R _ "UNIV :: nat set"])
  apply (simp add: ring_functions_axioms)
    using function_comp_is_closed_seq 
    apply (metis assms(1) assms(2) assms(3) fun_add_closed nat_seqs_def)
     apply (metis assms(1) assms(2) assms(3) function_comp_is_closed_seq nat_seqs_def seq_plus_closed)
  by (smt UNIV_eq_I assms(1) assms(2) assms(3) closed_seqs_memE comp_apply function_comp_is_closed_seq nat_seqs_def ring_functions.fun_add_eval_car ring_functions_axioms)

lemma function_mult_comp_is_seq_mult:
  assumes "s  closed_seqs R"
  assumes "f  carrier (Fun R)"
  assumes "g  carrier (Fun R)"
  shows "(f Fun R g)  s = (f  s) Rω (g  s)"
  apply(rule ring_functions.function_ring_car_eqI[of R _ "UNIV :: nat set"])
  apply (simp add: ring_functions_axioms)
  using function_comp_is_closed_seq 
  apply (metis assms(1) assms(2) assms(3) fun_mult_closed nat_seqs_def)
  apply (metis assms(1) assms(2) assms(3) function_comp_is_closed_seq nat_seqs_def seq_mult_closed)
  by (metis (no_types, lifting) assms(1) assms(2) assms(3) comp_apply function_comp_is_closed_seq nat_seqs_def ring_functions.function_mult_eval_car ring_functions.function_ring_car_closed ring_functions_axioms)

lemma seq_plus_simp:
  assumes "s  closed_seqs R"
  assumes "t  closed_seqs R"
  shows "(s Rω t) k = s k  t k"
  using assms unfolding nat_seqs_def 
  by (simp add: ring_functions.fun_add_eval_car ring_functions_axioms)

lemma seq_mult_simp:
  assumes "s  closed_seqs R"
  assumes "t  closed_seqs R"
  shows "(s Rω t) k = s k  t k"
  using assms unfolding nat_seqs_def 
  by (simp add: ring_functions.function_mult_eval_car ring_functions_axioms)

lemma seq_one_simp:
"𝟭Rω k = 𝟭"
  by (simp add: nat_seqs_def ring_functions.function_one_eval ring_functions_axioms)

lemma seq_zero_simp:
"𝟬Rω k = 𝟬"
  by (simp add: nat_seqs_def ring_functions.function_zero_eval ring_functions_axioms)

lemma(in U_function_ring) ring_id_seq_comp:
  assumes "s  closed_seqs R"
  shows "ring_id  s = s"
  apply(rule ring_functions.function_ring_car_eqI[of R _ "UNIV::nat set"])
  using ring_functions_axioms apply auto[1]
  apply (metis assms function_comp_is_closed_seq nat_seqs_def ring_id_closed)  
  apply (metis assms nat_seqs_def)
  by (simp add: assms closed_seqs_memE ring_id_eval)
  
lemma(in U_function_ring) ring_seq_smult_closed:
  assumes "s  closed_seqs R"
  assumes "a  carrier R"
  shows "a Rω s  closed_seqs R"
  apply(rule closed_seqs_memI) 
  by (metis assms(1) assms(2) closed_seqs_memE nat_seqs_def ring_functions.function_smult_closed ring_functions_axioms)

lemma(in U_function_ring) ring_seq_smult_eval:
  assumes "s  closed_seqs R"
  assumes "a  carrier R"
  shows "(a Rω s) k = a  (s k)"
  by (metis UNIV_I assms(1) assms(2) nat_seqs_def ring_functions.function_smult_eval ring_functions_axioms)

lemma(in U_function_ring) ring_seq_smult_comp_assoc:
  assumes "s  closed_seqs R"
  assumes "f  carrier (Fun R)"
  assumes "a  carrier R"
  shows "((a Fun R f)  s) = a Rω (f  s)"
  apply(rule ext)
  using function_smult_eval[of a f] ring_seq_smult_eval[of "f  s" a] 
  by (simp add: assms(1) assms(2) assms(3) closed_seqs_memE function_comp_is_closed_seq)
  
end 

(**************************************************************************************************)
(**************************************************************************************************)
section‹Extensional Maps Between the Carriers of two Structures›
(**************************************************************************************************)
(**************************************************************************************************)

definition struct_maps :: "('a, 'c) partial_object_scheme  ('b, 'd) partial_object_scheme 
                               ('a  'b) set" where
"struct_maps T S = {f. (f  (carrier T)  (carrier S))  f = restrict f (carrier T) }"

definition to_struct_map where
"to_struct_map T f = restrict f (carrier T)"

lemma to_struct_map_closed:
  assumes "f  (carrier T)  (carrier S)"
  shows "to_struct_map T f  (struct_maps T S)"
  by (smt PiE_restrict Pi_iff assms mem_Collect_eq restrict_PiE struct_maps_def to_struct_map_def)
  
lemma struct_maps_memI:
  assumes " x. x  carrier T  f x  carrier S"
  assumes "x. x  carrier T  f x = undefined"
  shows "f  struct_maps T S"
proof-
  have 0: " (f  (carrier T)  (carrier S))" 
    using assms 
    by blast
  have 1: "f  = restrict f (carrier T)"
    using assms 
    by (simp add: extensional_def extensional_restrict)
  show ?thesis 
    using 0 1 
    unfolding struct_maps_def 
    by blast   
qed

lemma struct_maps_memE:
  assumes "f  struct_maps T S"
  shows  " x. x  carrier T  f x  carrier S"
         "x. x  carrier T  f x = undefined"
  using assms unfolding struct_maps_def 
  apply blast
    using assms unfolding struct_maps_def 
    by (metis (mono_tags, lifting) mem_Collect_eq restrict_apply)

text‹An abbreviation for restricted composition of function of functions. This is necessary for the composition of two struct maps to again be a struct map.›
abbreviation(input) rcomp 
  where "rcomp  FuncSet.compose"

lemma struct_map_comp:
  assumes "g  (struct_maps T S)"
  assumes "f  (struct_maps S U)"
  shows "rcomp (carrier T) f g  (struct_maps T U)"
proof(rule struct_maps_memI)
  show "x. x  carrier T  rcomp (carrier T) f g x  carrier U"
    using assms struct_maps_memE(1) 
    by (metis compose_eq)    
  show " x. x  carrier T  rcomp (carrier T) f g x = undefined"
    by (meson compose_extensional extensional_arb)
qed

lemma r_comp_is_compose:
  assumes "g  (struct_maps T S)"
  assumes "f  (struct_maps S U)"
  assumes "a  (carrier T)"
  shows "(rcomp (carrier T) f g) a = (f  g) a"
  by (simp add: FuncSet.compose_def assms(3))

lemma r_comp_not_in_car:
  assumes "g  (struct_maps T S)"
  assumes "f  (struct_maps S U)"
  assumes "a  (carrier T)"
  shows "(rcomp (carrier T) f g) a = undefined"
  by (simp add: FuncSet.compose_def assms(3))

text‹The reverse composition of two struct maps:›

definition pullback ::
    "('a, 'd) partial_object_scheme  ('a  'b)  ('b  'c)  ('a  'c)" where
"pullback T f g = rcomp (carrier T) g f"

lemma pullback_closed:
  assumes "f  (struct_maps T S)"
  assumes "g  (struct_maps S U)"
  shows "pullback T f g  (struct_maps T U)"
  by (metis assms(1) assms(2) pullback_def struct_map_comp)

text‹Composition of struct maps which takes the structure itself rather than the carrier as a parameter:›

definition pushforward :: 
    "('a, 'd) partial_object_scheme  ('b  'c)  ('a  'b)   ('a  'c)" where
"pushforward T f g  rcomp (carrier T) f g"

lemma pushforward_closed:
  assumes "g  (struct_maps T S)"
  assumes "f  (struct_maps S U)"
  shows "pushforward T f g  (struct_maps T U)"
  using assms(1) assms(2) struct_map_comp 
  by (metis pushforward_def)
  

end

Theory Cring_Poly

theory Cring_Poly
  imports "HOL-Algebra.UnivPoly" "HOL-Algebra.Subrings" Function_Ring
begin

text‹
  This theory extends the material in theoryHOL-Algebra.UnivPoly. The main additions are 
  material on Taylor expansions of polynomials and polynomial derivatives, and various applications
  of the universal property of polynomial evaluation. These include construing polynomials as 
  functions from the base ring to itself, composing one polynomial with another, and extending
  homomorphisms between rings to homomoprhisms of their polynomial rings. These formalizations 
  are necessary components of the proof of Hensel's lemma for $p$-adic integers, and for the 
  proof of $p$-adic quantifier elimination. ›

lemma(in ring) ring_hom_finsum:
  assumes "h  ring_hom R S"
  assumes "ring S"
  assumes "finite I"
  assumes "F  I  carrier R"
  shows "h (finsum R F I) = finsum S (h  F) I"
proof- 
  have I: "(h  ring_hom R S  F  I  carrier R)  h (finsum R F I) = finsum S (h  F) I"
    apply(rule finite_induct, rule assms)
    using assms ring_hom_zero[of h R S] 
    apply (metis abelian_group_def abelian_monoid.finsum_empty is_ring ring_def)
  proof(rule)
    fix A a 
    assume A: "finite A" "a  A" "h  ring_hom R S  F  A  carrier R 
           h (finsum R F A) = finsum S (h  F) A" "h  ring_hom R S  F  insert a A  carrier R"
    have 0: "h  ring_hom R S  F  A  carrier R "
      using A by auto 
    have 1: "h (finsum R F A) = finsum S (h  F) A"
      using A 0 by auto 
    have 2: "abelian_monoid S"
      using assms ring_def abelian_group_def by auto
    have 3: "h (F a  finsum R F A) = h (F a) S (finsum S (h  F) A) "
      using ring_hom_add assms finsum_closed 1 A(4) by fastforce
    have 4: "finsum R F (insert a A) = F a  finsum R F A"
      using finsum_insert[of A a F] A assms by auto 
    have 5: "finsum S (h  F) (insert a A) = (h  F) a S finsum S (h  F) A"
      apply(rule abelian_monoid.finsum_insert[of S A a "h  F"])
          apply (simp add: "2")
         apply(rule A)
        apply(rule A)
      using ring_hom_closed A  "0" apply fastforce
      using A ring_hom_closed by auto 
    show "h (finsum R F (insert a A)) =
           finsum S (h  F) (insert a A)"
      unfolding 4 5 3 by auto 
  qed
  thus ?thesis using assms by blast 
qed

lemma(in ring) ring_hom_a_inv:
  assumes "ring S"
  assumes "h  ring_hom R S"
  assumes "b  carrier R"
  shows "h ( b) =  S h b"
proof-
  have "h b S h ( b) = 𝟬S"
    by (metis (no_types, hide_lams) abelian_group.a_inv_closed assms(1) assms(2) assms(3) 
        is_abelian_group local.ring_axioms r_neg ring_hom_add ring_hom_zero)        
  then show ?thesis 
    by (metis (no_types, lifting) abelian_group.minus_equality add.inv_closed assms(1) 
        assms(2) assms(3) ring.is_abelian_group ring.ring_simprules(10) ring_hom_closed)
qed

lemma(in ring) ring_hom_minus:
  assumes "ring S"
  assumes "h  ring_hom R S"
  assumes "a  carrier R"
  assumes "b  carrier R"
  shows "h (a  b) = h a S h b" 
  using assms ring_hom_add[of h R S a "R b"]
  unfolding a_minus_def 
  using ring_hom_a_inv[of S h b] by auto 

lemma ring_hom_nat_pow:
  assumes "ring R"
  assumes "ring S"
  assumes "h  ring_hom R S"
  assumes "a  carrier R"
  shows "h (a[^]R(n::nat)) = (h a)[^]S(n::nat)"
  using assms by (simp add: ring_hom_ring.hom_nat_pow ring_hom_ringI2)

lemma (in ring) Units_not_right_zero_divisor:
  assumes "a  Units R"
  assumes "b  carrier R"
  assumes "a  b = 𝟬"
  shows  "b = 𝟬"
proof- 
  have "inv a  a  b = 𝟬 "
    using assms Units_closed Units_inv_closed r_null m_assoc[of "inv a" a b] by presburger
  thus ?thesis using assms 
    by (metis Units_l_inv l_one)    
qed

lemma (in ring) Units_not_left_zero_divisor:
  assumes "a  Units R"
  assumes "b  carrier R"
  assumes "b  a = 𝟬"
  shows  "b = 𝟬"
proof- 
  have "b  (a  inv a) = 𝟬 "
    using assms Units_closed Units_inv_closed l_null m_assoc[of b a"inv a"] by presburger
  thus ?thesis using assms 
    by (metis Units_r_inv r_one)    
qed

lemma (in cring) finsum_remove:
  assumes "i. i  Y  f i  carrier R"
  assumes "finite Y"
  assumes "i  Y"
  shows "finsum R f Y = f i  finsum R f (Y - {i})"
proof- 
  have "finsum R f (insert i (Y - {i})) = f i  finsum R f (Y - {i})"
    apply(rule finsum_insert)
    using assms apply blast apply blast using assms apply blast
    using assms by blast 
  thus ?thesis using assms 
    by (metis insert_Diff)
qed


type_synonym degree = nat
text‹The composition of two ring homomorphisms is a ring homomorphism›
lemma ring_hom_compose:                                                                                                           
  assumes "ring R"
  assumes "ring S"
  assumes "ring T"
  assumes "h  ring_hom R S"
  assumes "g  ring_hom S T"
  assumes "c. c  carrier R  f c = g (h c)"
  shows "f   ring_hom R T"
proof(rule ring_hom_memI)
  show "x. x  carrier R  f x  carrier T"
    using assms  by (metis ring_hom_closed)
  show " x y. x  carrier R  y  carrier R  f (x R y) = f x T f y"
  proof-
    fix x y
    assume A: "x   carrier R" "y   carrier R"
    show "f (x R y) = f x T f y"
    proof-
      have  "f (x R y) = g (h (x R y))"
        by (simp add: A(1) A(2) assms(1) assms(6) ring.ring_simprules(5))
      then have  "f (x R y) = g ((h x) S (h y))"
        using A(1) A(2) assms(4) ring_hom_mult by fastforce
      then have  "f (x R y) = g (h x) T g (h y)"
        using A(1) A(2) assms(4) assms(5) ring_hom_closed ring_hom_mult by fastforce
      then show ?thesis 
        by (simp add: A(1) A(2) assms(6))
    qed
  qed
  show "x y. x  carrier R  y  carrier R  f (x R y) = f x T f y"
  proof-
    fix x y
    assume A: "x   carrier R" "y   carrier R"
    show "f (x R y) = f x T f y"
    proof-
      have  "f (x R y) = g (h (x R y))"
        by (simp add: A(1) A(2) assms(1) assms(6) ring.ring_simprules(1))
      then have  "f (x R y) = g ((h x) S (h y))"
        using A(1) A(2) assms(4) ring_hom_add by fastforce
      then have  "f (x R y) = g (h x) T g (h y)"
        by (metis (no_types, hide_lams) A(1) A(2) assms(4) assms(5) ring_hom_add ring_hom_closed)
      then show ?thesis
        by (simp add: A(1) A(2) assms(6))
    qed
  qed
  show "f 𝟭R = 𝟭T" 
    by (metis assms(1) assms(4) assms(5) assms(6) ring.ring_simprules(6) ring_hom_one)
qed


(**************************************************************************************************)
(**************************************************************************************************)
section‹Basic Notions about Polynomials›
(**************************************************************************************************)
(**************************************************************************************************)
context UP_ring
begin


text‹rings are closed under monomial terms›
lemma monom_term_car:
  assumes "c  carrier R"
  assumes "x  carrier R"
  shows "c  x[^](n::nat)  carrier R"
  using assms monoid.nat_pow_closed 
  by blast

text‹Univariate polynomial ring over R›

lemma P_is_UP_ring:
"UP_ring R"
  by (simp add: UP_ring_axioms)

text‹Degree function›
abbreviation(input) degree  where
"degree f   deg R f"

lemma UP_car_memI:
  assumes "n. n > k  p n = 𝟬"
  assumes "n. p n  carrier R"
  shows "p  carrier P"
proof-
  have "bound 𝟬 k p"
    by (simp add: assms(1) bound.intro)
  then show ?thesis 
    by (metis (no_types, lifting) P_def UP_def assms(2) mem_upI partial_object.select_convs(1))
qed

lemma(in UP_cring) UP_car_memI':
  assumes "x. g x  carrier R"
  assumes "x. x > k  g x = 𝟬"
  shows "g  carrier (UP R)"
proof-
  have "bound 𝟬 k g"
    using assms unfolding bound_def by blast 
  then show ?thesis 
    using P_def UP_car_memI assms(1) by blast
qed

lemma(in UP_cring) UP_car_memE:
  assumes "g  carrier (UP R)"
  shows "x. g x  carrier R"
        "x. x > (deg R g)  g x = 𝟬"
  using P_def assms UP_def[of R] apply (simp add: mem_upD)
  using assms UP_def[of R] up_def[of R] 
  by (smt R.ring_axioms UP_ring.deg_aboveD UP_ring.intro partial_object.select_convs(1) restrict_apply up_ring.simps(2))
  
end

  (**************************************************************************************************)
  (**************************************************************************************************)
  subsection‹Lemmas About Coefficients›
  (**************************************************************************************************)
  (**************************************************************************************************)

context UP_ring
begin
text‹The goal here is to reduce dependence on the function coeff from Univ\_Poly, in favour of using
a polynomial itself as its coefficient function.›

lemma coeff_simp:
  assumes "f  carrier P"
  shows "coeff (UP R)  f = f "
proof fix x show "coeff (UP R) f x = f x" 
    using assms P_def UP_def[of R] by auto 
qed

text‹Coefficients are in R›

lemma cfs_closed:
  assumes "f  carrier P"
  shows "f n  carrier R"
  using assms coeff_simp[of f]  P_def coeff_closed 
  by fastforce

lemma cfs_monom:
  "a  carrier R ==> (monom P a m) n = (if m=n then a else 𝟬)"
using coeff_simp  P_def coeff_monom monom_closed by auto

lemma cfs_zero [simp]: "𝟬P n = 𝟬" 
  using P_def UP_zero_closed coeff_simp coeff_zero by auto

lemma cfs_one [simp]: "𝟭P n = (if n=0 then 𝟭 else 𝟬)"
  by (metis P_def R.one_closed UP_ring.cfs_monom UP_ring_axioms monom_one)

lemma cfs_smult [simp]:
  "[| a  carrier R; p  carrier P |] ==> (a P p) n = a  p n"
  using P_def UP_ring.coeff_simp UP_ring_axioms UP_smult_closed coeff_smult by fastforce
  
lemma cfs_add [simp]:
  "[| p  carrier P; q  carrier P |] ==> (p P q) n = p n  q n"
  by (metis P.add.m_closed P_def UP_ring.coeff_add UP_ring.coeff_simp UP_ring_axioms)

lemma cfs_a_inv [simp]:
  assumes R: "p  carrier P"
  shows "(P p) n =  (p n)"
  using P.add.inv_closed P_def UP_ring.coeff_a_inv UP_ring.coeff_simp UP_ring_axioms assms 
  by fastforce

lemma cfs_minus [simp]:
  "[| p  carrier P; q  carrier P |] ==> (p P q) n = p n  q n"
  using P.minus_closed P_def coeff_minus coeff_simp by auto

lemma cfs_monom_mult_r:
  assumes "p  carrier P"
  assumes "a  carrier R"
  shows "(monom P a n P p) (k + n) = a  p k"
  using coeff_monom_mult assms P.m_closed P_def coeff_simp monom_closed by auto

lemma(in UP_cring) cfs_monom_mult_l:
  assumes "p  carrier P"
  assumes "a  carrier R"
  shows "(p P monom P a n) (k + n) = a  p k"
  using UP_m_comm assms(1) assms(2) cfs_monom_mult_r by auto

lemma(in UP_cring) cfs_monom_mult_l':
  assumes "f  carrier P"
  assumes "a  carrier R"
  assumes "m  n"
  shows "(f P (monom P a n)) m = a  (f (m - n))"
  using cfs_monom_mult_l[of f a n "m-n"] assms 
  by simp

lemma(in UP_cring) cfs_monom_mult_r':
  assumes "f  carrier P"
  assumes "a  carrier R"
  assumes "m  n"
  shows "((monom P a n) P f) m = a  (f (m - n))"
  using cfs_monom_mult_r[of f a n "m-n"] assms 
  by simp 
end
  (**************************************************************************************************)
  (**************************************************************************************************)
  subsection‹Degree Bound Lemmas›
  (**************************************************************************************************)
  (**************************************************************************************************)

context UP_ring
begin

lemma bound_deg_sum:
  assumes " f  carrier P"
  assumes "g  carrier P"
  assumes "degree f   n"
  assumes "degree g  n"
  shows "degree (f P g)  n"
  using P_def UP_ring_axioms assms(1) assms(2) assms(3) assms(4) 
  by (meson deg_add max.boundedI order_trans)

lemma bound_deg_sum':
  assumes " f  carrier P"
  assumes "g  carrier P"
  assumes "degree f < n"
  assumes "degree g < n"
  shows "degree (f P g) < n"
  using P_def UP_ring_axioms assms(1) assms(2) 
        assms(3) assms(4) 
  by (metis bound_deg_sum le_neq_implies_less less_imp_le_nat not_less)        

lemma equal_deg_sum:
  assumes " f  carrier P"
  assumes "g  carrier P"
  assumes "degree f < n"
  assumes "degree g = n"
  shows "degree (f P g) = n"
proof-
  have 0: "degree (f P g) n"
    using assms bound_deg_sum 
          P_def UP_ring_axioms by auto
  show "degree (f P g) = n"
  proof(rule ccontr)
    assume "degree (f P g)  n "
    then have 1: "degree (f P g) < n"
      using 0 by auto 
    have 2: "degree (P f) < n"
      using assms by simp
    have 3: "g = (f P g) P (P f)"
      using assms 
      by (simp add: P.add.m_comm P.r_neg1)
    then show False using 1 2 3 assms 
      by (metis UP_a_closed UP_a_inv_closed deg_add leD le_max_iff_disj)
  qed
qed

lemma equal_deg_sum':
  assumes "f  carrier P"
  assumes "g  carrier P"
  assumes "degree g < n"
  assumes "degree f = n"
  shows "degree (f P g) = n"
  using P_def UP_a_comm UP_ring.equal_deg_sum UP_ring_axioms assms(1) assms(2) assms(3) assms(4)
  by fastforce

lemma degree_of_sum_diff_degree:
  assumes "p  carrier P"
  assumes "q  carrier P"
  assumes "degree q < degree p"
  shows "degree (p P q) = degree p" 
  by(rule equal_deg_sum', auto simp: assms)

lemma degree_of_difference_diff_degree:
  assumes "p  carrier P"
  assumes "q  carrier P"
  assumes "degree q < degree p"
  shows "degree (p P q) = degree p" 
proof-
  have A: "(p P q) = p P (P q)" 
    by (simp add: P.minus_eq)
  have "degree (P q) = degree q " 
    by (simp add: assms(2))
  then show ?thesis 
    using assms A 
    by (simp add: degree_of_sum_diff_degree)
qed

lemma (in UP_ring) deg_diff_by_const:
  assumes "g  carrier (UP R)"
  assumes "a  carrier R"
  assumes "h = g UP R up_ring.monom (UP R) a 0"
  shows "deg R g = deg R h"
  unfolding assms using assms 
  by (metis P_def UP_ring.bound_deg_sum UP_ring.deg_monom_le UP_ring.monom_closed UP_ring_axioms degree_of_sum_diff_degree gr_zeroI not_less)

lemma (in UP_ring) deg_diff_by_const':
  assumes "g  carrier (UP R)"
  assumes "a  carrier R"
  assumes "h = g UP R up_ring.monom (UP R) a 0"
  shows "deg R g = deg R h"
  apply(rule deg_diff_by_const[of _  " a"])
  using assms apply blast 
  using assms apply blast
  by (metis P.minus_eq P_def assms(2) assms(3) monom_a_inv)

lemma(in UP_ring) deg_gtE:
  assumes "p  carrier P"
  assumes "i > deg R p"
  shows "p i = 𝟬"
  using assms  P_def coeff_simp deg_aboveD by metis
end

  (**************************************************************************************************)
  (**************************************************************************************************)
  subsection‹Leading Term Function›
  (**************************************************************************************************)
  (**************************************************************************************************)

definition leading_term where 
"leading_term R f = monom (UP R) (f (deg R f)) (deg R f)"

context UP_ring
begin

abbreviation(input) ltrm  where
"ltrm f  monom P (f (deg R f)) (deg R f)"

text‹leading term is a polynomial›
lemma ltrm_closed:
  assumes "f  carrier P"
  shows "ltrm f  carrier P"
  using assms 
  by (simp add: cfs_closed)  
  
text‹Simplified coefficient function description for leading term›
lemma ltrm_coeff:
  assumes "f  carrier P"
  shows "coeff P (ltrm f) n  = (if (n = degree f) then (f (degree f)) else 𝟬)"
    using assms 
    by (simp add: cfs_closed)

lemma ltrm_cfs:
  assumes "f  carrier P"
  shows "(ltrm f) n  = (if (n = degree f) then (f (degree f)) else 𝟬)"
  using assms 
  by (simp add: cfs_closed cfs_monom)

lemma ltrm_cfs_above_deg:
  assumes "f  carrier P"
  assumes "n > degree f"
  shows "ltrm f n = 𝟬"
  using assms 
  by (simp add: ltrm_cfs)

text‹The leading term of f has the same degree as f›

lemma deg_ltrm:
  assumes "f  carrier P"
  shows "degree (ltrm f) = degree f"
  using assms 
  by (metis P_def UP_ring.lcoeff_nonzero_deg UP_ring_axioms cfs_closed coeff_simp deg_const deg_monom)

text‹Subtracting the leading term yields a drop in degree›

lemma minus_ltrm_degree_drop:
  assumes "f  carrier P"
  assumes "degree f = Suc n"
  shows "degree (f P (ltrm f))  n"
proof(rule UP_ring.deg_aboveI)
  show C0: "UP_ring R" 
    by (simp add: UP_ring_axioms)    
  show C1: "f P ltrm f  carrier (UP R)"
    using assms ltrm_closed P.minus_closed P_def 
    by blast     
  show C2: "m. n < m  coeff (UP R) (f P ltrm f) m = 𝟬"
  proof-
    fix m
    assume A: "n<m"
    show "coeff (UP R) (f P ltrm f) m = 𝟬"
    proof(cases " m = Suc n")
      case True
      have B: "f m  carrier R" 
        using UP.coeff_closed P_def assms(1) cfs_closed by blast       
      have "m = degree f"
        using True by (simp add: assms(2))
      then have "f m = (ltrm f) m" 
        using ltrm_cfs assms(1) by auto 
      then have "(f m) R( ltrm f) m = 𝟬"
        using B UP_ring_def P_is_UP_ring 
          B R.add.r_inv R.is_abelian_group abelian_group.minus_eq by fastforce
      then have "(f UP R ltrm f) m = 𝟬"
        by (metis C1 ltrm_closed P_def assms(1) coeff_minus coeff_simp)        
      then show ?thesis 
        using C1 P_def UP_ring.coeff_simp UP_ring_axioms by fastforce
    next
      case False
      have D0: "m > degree f" using False 
        using A assms(2) by linarith
       have B: "f m  carrier R" 
         using UP.coeff_closed P_def assms(1) cfs_closed 
         by blast
       have "f m = (ltrm f) m" 
         using D0 ltrm_cfs_above_deg P_def assms(1) coeff_simp deg_aboveD 
         by auto 
      then show ?thesis 
        by (metis B ltrm_closed P_def R.r_neg UP_ring.coeff_simp UP_ring_axioms a_minus_def assms(1) coeff_minus)       
    qed
  qed
qed

lemma ltrm_decomp:
  assumes "f  carrier P"
  assumes "degree f >(0::nat)"
  obtains g where "g  carrier P  f = g P (ltrm f)  degree g < degree f"
proof-
  have 0: "f P (ltrm f)  carrier P"
    using ltrm_closed assms(1) by blast    
  have 1: "f = (f P (ltrm f)) P (ltrm f)"
    using assms 
    by (metis "0" ltrm_closed P.add.inv_solve_right P.minus_eq)
  show ?thesis using assms 0 1 minus_ltrm_degree_drop[of f]
    by (metis ltrm_closed Suc_diff_1 Suc_n_not_le_n deg_ltrm equal_deg_sum' linorder_neqE_nat that)
qed

text‹leading term of a sum›
lemma coeff_of_sum_diff_degree0:
  assumes "p  carrier P"
  assumes "q  carrier P"
  assumes "degree q < n"
  shows "(p P q) n = p n"
  using assms P_def UP_ring.deg_aboveD UP_ring_axioms cfs_add coeff_simp cfs_closed deg_aboveD 
  by auto

lemma coeff_of_sum_diff_degree1:
  assumes "p  carrier P"
  assumes "q  carrier P"
  assumes "degree q < degree p"
  shows "(p P q) (degree p) = p (degree p)"
  using assms(1) assms(2) assms(3) coeff_of_sum_diff_degree0 by blast



lemma ltrm_of_sum_diff_degree:
  assumes "p  carrier P"
  assumes "q  carrier P"
  assumes "degree p > degree q"
  shows "ltrm (p P q) = ltrm p" 
  unfolding leading_term_def 
  using assms(1) assms(2) assms(3) coeff_of_sum_diff_degree1 degree_of_sum_diff_degree 
  by presburger

text‹leading term of a monomial›

lemma ltrm_monom:
  assumes "a  carrier R"
  assumes "f = monom P a n"
  shows "ltrm f = f"
  unfolding leading_term_def
  by (metis P_def UP_ring.cfs_monom UP_ring.monom_zero UP_ring_axioms assms(1) assms(2) deg_monom)  

lemma ltrm_monom_simp:
  assumes "a  carrier R"
  shows "ltrm (monom P a n) = monom P a n"
  using assms ltrm_monom by auto

lemma ltrm_inv_simp[simp]:
  assumes "f  carrier P"
  shows "ltrm (ltrm f) = ltrm f"
  by (metis assms deg_ltrm ltrm_cfs)

lemma ltrm_deg_0:
  assumes "p  carrier P"
  assumes "degree p = 0"
  shows "ltrm p = p"
  using ltrm_monom assms P_def UP_ring.deg_zero_impl_monom UP_ring_axioms coeff_simp
  by fastforce

lemma ltrm_prod_ltrm:
  assumes "p  carrier P"
  assumes "q  carrier P"
  shows "ltrm ((ltrm p) P (ltrm q)) = (ltrm p) P (ltrm q)"
  using ltrm_monom R.m_closed assms(1) assms(2) cfs_closed monom_mult
  by metis 

text‹lead coefficient function›

abbreviation(input) lcf where
"lcf p  p (deg R p)"

lemma(in UP_ring) lcf_ltrm:
"ltrm p = monom P (lcf p) (degree p)"
  by auto

lemma lcf_closed:
  assumes "f  carrier P"
  shows "lcf f  carrier R"
  by (simp add: assms cfs_closed)

lemma(in UP_cring) lcf_monom:
  assumes "a  carrier R"
  shows "lcf (monom P a n) = a" "lcf (monom (UP R) a n) = a"
  using assms deg_monom cfs_monom apply fastforce
  by (metis UP_ring.cfs_monom UP_ring.deg_monom UP_ring_axioms assms)


end 

text‹Function which truncates a polynomial by removing the leading term›

definition truncate where
"truncate R f = f (UP R) (leading_term R f)"

context UP_ring
begin 

abbreviation(input) trunc where
"trunc  truncate R"

lemma trunc_closed:
  assumes "f  carrier P"
  shows "trunc f  carrier P"
  using assms unfolding truncate_def 
  by (metis ltrm_closed P_def UP_ring.UP_ring UP_ring_axioms leading_term_def ring.ring_simprules(4))

lemma trunc_simps:
  assumes "f  carrier P"
  shows "f = (trunc f) P (ltrm f)"
        "f P (trunc f) = ltrm f"   
  apply (metis ltrm_closed P.add.inv_solve_right P.minus_closed P_def a_minus_def assms Cring_Poly.truncate_def leading_term_def)
  using trunc_closed[of f] ltrm_closed[of f] P_def P.add.inv_solve_right[of "ltrm f" f "trunc f"]
        assms  unfolding UP_cring_def
  by (metis P.add.inv_closed P.add.m_lcomm P.add.r_inv_ex P.minus_eq P.minus_minus
      P.r_neg2 P.r_zero Cring_Poly.truncate_def leading_term_def)

lemma trunc_zero:
  assumes "f  carrier P"
  assumes "degree f = 0"
  shows "trunc f = 𝟬P"
  unfolding truncate_def 
  using assms ltrm_deg_0[of f] 
  by (metis P.r_neg P_def a_minus_def leading_term_def)

lemma trunc_degree:
  assumes "f  carrier P"
  assumes "degree f > 0"
  shows "degree (trunc f) < degree f"
  unfolding truncate_def using assms 
  by (metis ltrm_closed ltrm_decomp P.add.right_cancel Cring_Poly.truncate_def trunc_closed trunc_simps(1))

text‹The coefficients of trunc agree with f for small degree›

lemma trunc_cfs:
  assumes "p  carrier P"
  assumes "n < degree p"
  shows " (trunc p) n = p n"
  using P_def assms(1) assms(2) unfolding truncate_def 
  by (smt ltrm_closed ltrm_cfs R.minus_zero R.ring_axioms UP_ring.cfs_minus
      UP_ring_axioms a_minus_def cfs_closed leading_term_def nat_neq_iff ring.ring_simprules(15))

text‹monomial predicate›

definition is_UP_monom where
"is_UP_monom = (λf. f  carrier (UP R)  f = ltrm f)"

lemma is_UP_monomI:
  assumes "a  carrier R"
  assumes "p = monom P a n"
  shows "is_UP_monom p"
  using assms(1) assms(2) is_UP_monom_def ltrm_monom P_def monom_closed 
  by auto
  
lemma is_UP_monomI':
  assumes "f  carrier (UP R)"
  assumes "f = ltrm f"
  shows "is_UP_monom f"
  using assms P_def unfolding is_UP_monom_def by blast 

lemma monom_is_UP_monom:
  assumes "a  carrier R"
  shows "is_UP_monom (monom P a n)" "is_UP_monom (monom (UP R) a n)"
  using assms P_def ltrm_monom_simp monom_closed
  unfolding is_UP_monom_def  
  by auto

lemma is_UP_monomE:
  assumes "is_UP_monom f"
  shows "f  carrier P" "f = monom P (lcf f) (degree f)"  "f = monom (UP R) (lcf f) (degree f)"
  using assms unfolding is_UP_monom_def 
  by(auto simp: P_def ) 

lemma ltrm_is_UP_monom:
  assumes "p  carrier P"
  shows "is_UP_monom (ltrm p)"
  using assms 
  by (simp add: cfs_closed monom_is_UP_monom(1))

lemma is_UP_monom_mult:
  assumes "is_UP_monom p"
  assumes "is_UP_monom q"
  shows "is_UP_monom (p P q)"
  apply(rule is_UP_monomI')
  using assms is_UP_monomE P_def UP_mult_closed 
  apply simp  
  using assms is_UP_monomE[of p] is_UP_monomE[of q] 
        P_def monom_mult 
  by (metis lcf_closed ltrm_monom R.m_closed)
end 

  (**************************************************************************************************)
  (**************************************************************************************************)
  subsection‹Properties of Leading Terms and Leading Coefficients in Commutative Rings and Domains›
  (**************************************************************************************************)
  (**************************************************************************************************)

context UP_cring
begin

lemma cring_deg_mult:
  assumes "q  carrier P"
  assumes "p  carrier P"
  assumes "lcf q  lcf p 𝟬"
  shows "degree (q P p) = degree p + degree q"
proof-
  have "q P p = (trunc q P ltrm q) P (trunc p P ltrm p)"
    using assms(1) assms(2) trunc_simps(1) by auto
  then have "q P p = (trunc q P ltrm q) P (trunc p P ltrm p)"
    by linarith
  then have 0: "q P p = (trunc q  P (trunc p P ltrm p)) P ( ltrm q P (trunc p P ltrm p))"
    by (simp add: P.l_distr assms(1) assms(2) ltrm_closed trunc_closed)
  have 1: "(trunc q  P (trunc p P ltrm p)) (degree p + degree q) = 𝟬"
  proof(cases "degree q = 0")
    case True
    then show ?thesis 
      using assms(1) assms(2) trunc_simps(1) trunc_zero by auto           
  next
    case False
    have "degree ((trunc q)  P p)  degree (trunc q) + degree p"
      using assms trunc_simps[of q]  deg_mult_ring[of "trunc q" p]  trunc_closed 
      by blast      
    then have "degree (trunc q  P (trunc p P ltrm p)) < degree q + degree p"
      using False assms(1) assms(2) trunc_degree trunc_simps(1) by fastforce
    then show ?thesis 
      by (metis P_def UP_mult_closed UP_ring.coeff_simp UP_ring_axioms
          add.commute assms(1) assms(2) deg_belowI not_less trunc_closed trunc_simps(1))      
  qed
  have 2: "(q P p) (degree p + degree q) = 
                   ( ltrm q P (trunc p P ltrm p)) (degree p + degree q)"
    using 0 1 assms cfs_closed trunc_closed by auto       
  have 3: "(q P p) (degree p + degree q) = 
                   ( ltrm q P trunc p) (degree p + degree q)  ( ltrm q P ltrm p) (degree p + degree q)"
    by (simp add: "2" ltrm_closed UP_r_distr assms(1) assms(2) trunc_closed)   
  have 4: "( ltrm q P trunc p) (degree p + degree q) = 𝟬"
  proof(cases "degree p = 0")
    case True
    then show ?thesis 
      using "2" "3" assms(1) assms(2) cfs_closed ltrm_closed trunc_zero by auto            
  next
    case False
    have "degree ( ltrm q P trunc p)  degree (ltrm q) + degree (trunc p)"
      using assms trunc_simps deg_mult_ring ltrm_closed trunc_closed by presburger              
    then have "degree ( ltrm q P trunc p) < degree q + degree p"
      using False assms(1) assms(2) trunc_degree trunc_simps(1) deg_ltrm by fastforce 
    then show ?thesis 
      by (metis ltrm_closed P_def UP_mult_closed UP_ring.coeff_simp UP_ring_axioms add.commute assms(1) assms(2) deg_belowI not_less trunc_closed)          
  qed
  have 5: "(q P p) (degree p + degree q) = ( ltrm q P ltrm p) (degree p + degree q)"
    by (simp add: "3" "4" assms(1) assms(2) cfs_closed)   
  have 6: "ltrm q P ltrm p = monom P (lcf q  lcf p) (degree p + degree q)"
    unfolding  leading_term_def 
    by (metis P_def UP_ring.monom_mult UP_ring_axioms add.commute assms(1) assms(2) cfs_closed)
  have 7: "( ltrm q P ltrm p) (degree p + degree q) 𝟬"
    using 5  6 assms 
    by (metis R.m_closed cfs_closed cfs_monom)   
  have 8: "degree (q P p) degree p + degree q"
    using 5 6 7 P_def UP_mult_closed assms(1) assms(2) 
    by (simp add: UP_ring.coeff_simp UP_ring_axioms deg_belowI)    
  then show ?thesis 
    using assms(1) assms(2) deg_mult_ring by fastforce
qed

text‹leading term is multiplicative›

lemma ltrm_of_sum_diff_deg:
  assumes "q  carrier P"
  assumes "a  carrier R"
  assumes "a 𝟬"
  assumes "degree q < n"
  assumes "p = q P (monom P a n)"
  shows "ltrm p =  (monom P a n)"
proof-
  have 0: "degree  (monom P a n) = n" 
    by (simp add: assms(2) assms(3))
  have 1: "(monom P a n)  carrier P"
    using assms(2) by auto
  have 2: "ltrm ((monom P a n) P q) = ltrm (monom P a n)"
    using assms ltrm_of_sum_diff_degree[of "(monom P a n)" q] 1  "0" by linarith
  then show ?thesis 
    using UP_a_comm assms(1) assms(2) assms(5) ltrm_monom by auto
qed

lemma(in UP_cring) ltrm_smult_cring:
  assumes "p  carrier P"
  assumes "a  carrier R"
  assumes "lcf p  a  𝟬"
  shows "ltrm (a Pp) = aP(ltrm p)"
  using assms 
  by (smt lcf_monom(1) P_def R.m_closed R.m_comm cfs_closed cfs_smult coeff_simp
      cring_deg_mult deg_monom deg_ltrm monom_closed monom_mult_is_smult monom_mult_smult) 

lemma(in UP_cring) deg_zero_ltrm_smult_cring:
  assumes "p  carrier P"
  assumes "a  carrier R"
  assumes "degree p = 0"
  shows "ltrm (a Pp) = aP(ltrm p)"
  by (metis ltrm_deg_0 assms(1) assms(2) assms(3) deg_smult_decr le_0_eq module.smult_closed module_axioms)

lemma(in UP_domain) ltrm_smult:
  assumes "p  carrier P"
  assumes "a  carrier R"
  shows "ltrm (a Pp) = aP(ltrm p)"
  by (metis lcf_closed ltrm_closed ltrm_smult_cring P_def R.integral_iff UP_ring.deg_ltrm 
      UP_ring_axioms UP_smult_zero assms(1) assms(2) cfs_zero deg_nzero_nzero deg_zero_ltrm_smult_cring monom_zero)

lemma(in UP_cring) cring_ltrm_mult:
  assumes "p  carrier P"
  assumes "q  carrier P"
  assumes "lcf p  lcf q  𝟬"
  shows "ltrm (p P q) = (ltrm p) P (ltrm q)"
proof(cases "degree p = 0  degree q = 0")
  case True
  then show ?thesis 
    by (smt ltrm_closed ltrm_deg_0 ltrm_smult_cring R.m_comm UP_m_comm assms(1) assms(2) assms(3) cfs_closed monom_mult_is_smult)
next
  case False
    obtain q0 where q0_def: "q0 = trunc q" 
      by simp
    obtain p0 where p0_def: "p0 = trunc p" 
      by simp
    have Pq: "degree q0 < degree q"
      using False P_def assms(2) q0_def trunc_degree by blast          
    have Pp: "degree p0 < degree p"
      using False P_def assms(1) p0_def trunc_degree by blast
    have "p P q = (p0 P ltrm(p)) P (q0 P ltrm(q))"
      using assms(1) assms(2) p0_def q0_def trunc_simps(1) by auto
    then have P0: "p P q = ((p0 P ltrm(p)) P q0) P ((p0 P ltrm(p))P ltrm(q))"
      by (simp add: P.r_distr assms(1) assms(2) ltrm_closed p0_def q0_def trunc_closed)
    have P1: "degree ((p0 P ltrm(p)) P q0) < degree ((p0 P ltrm(p))P ltrm(q))"
    proof-
      have LHS: "degree ((p0 P ltrm(p)) P q0)  degree p + degree q0 "
      proof(cases "q0 = 𝟬P")
        case True
        then show ?thesis 
          using assms(1) p0_def trunc_simps(1) by auto
      next
        case False
        then show ?thesis 
          using assms(1) assms(2) deg_mult_ring  p0_def 
            q0_def trunc_simps(1) trunc_closed by auto
      qed
      have RHS: "degree ((p0 P ltrm(p))P ltrm(q)) = degree p + degree q"
        using assms(1) assms(2) deg_mult_ring ltrm_closed p0_def trunc_simps(1) 
        by (smt P_def UP_cring.lcf_monom(1) UP_cring.cring_deg_mult UP_cring_axioms add.commute assms(3) cfs_closed deg_ltrm)                    
        then show ?thesis 
          using RHS LHS  Pq 
          by linarith    
    qed
    then have P2: "ltrm (p P q) = ltrm ((p0 P ltrm(p))P ltrm(q))"
      using P0 P1  
      by (metis (no_types, lifting) ltrm_closed ltrm_of_sum_diff_degree P.add.m_comm 
          UP_mult_closed assms(1) assms(2) p0_def q0_def trunc_closed trunc_simps(1))      
    have P3: " ltrm ((p0 P ltrm(p))P ltrm(q)) = ltrm p P ltrm q"
    proof-
      have Q0: "((p0 P ltrm(p))P ltrm(q)) = (p0 P ltrm(q)) P  (ltrm(p))P ltrm(q)"
        by (simp add: P.l_distr assms(1) assms(2) ltrm_closed p0_def trunc_closed)
      have Q1: "degree ((p0 P ltrm(q)) ) < degree ((ltrm(p))P ltrm(q))"
      proof(cases "p0 = 𝟬P")
        case True
        then show ?thesis 
          using P1 assms(1) assms(2)  ltrm_closed by auto
      next
        case F: False
        then show ?thesis
          proof-
            have LHS: "degree ((p0 P ltrm(q))) < degree p + degree q"
              using False F  Pp assms(1) assms(2) deg_nzero_nzero 
                 deg_ltrm ltrm_closed p0_def trunc_closed 
              by (smt add_le_cancel_right deg_mult_ring le_trans not_less)   
            have RHS: "degree ((ltrm(p))P ltrm(q)) = degree p + degree q" 
              using cring_deg_mult[of "ltrm p" "ltrm q"] assms 
              by (simp add: ltrm_closed ltrm_cfs deg_ltrm)                      
            then show ?thesis using LHS RHS by auto 
        qed
      qed
      have Q2: "ltrm ((p0 P ltrm(p))P ltrm(q)) = ltrm ((ltrm(p))P ltrm(q))" 
        using Q0 Q1 
        by (metis (no_types, lifting) ltrm_closed ltrm_of_sum_diff_degree P.add.m_comm 
            UP_mult_closed assms(1) assms(2) p0_def trunc_closed)        
      show ?thesis using ltrm_prod_ltrm Q0 Q1 Q2 
        by (simp add: assms(1) assms(2))
    qed
    then show ?thesis 
      by (simp add: P2)
qed

lemma(in UP_domain) ltrm_mult:
  assumes "p  carrier P"
  assumes "q  carrier P"
  shows "ltrm (p P q) = (ltrm p) P (ltrm q)"
  using cring_ltrm_mult assms 
  by (smt ltrm_closed ltrm_deg_0 cfs_closed deg_nzero_nzero deg_ltrm local.integral_iff monom_mult monom_zero)

lemma lcf_deg_0:
  assumes "degree p = 0"
  assumes "p  carrier P"
  assumes "q  carrier P"
  shows "(p P q) = (lcf p)Pq" 
  using P_def assms(1) assms(2) assms(3) 
  by (metis ltrm_deg_0 cfs_closed monom_mult_is_smult)
  
text‹leading term powers›

lemma (in domain) nonzero_pow_nonzero:
  assumes "a  carrier R"
  assumes "a 𝟬"
  shows "a[^](n::nat)  𝟬"  
proof(induction n)
  case 0
  then show ?case 
    by auto
next
  case (Suc n)
  fix n::nat
  assume IH: "a[^] n  𝟬" 
  show "a[^] (Suc n)  𝟬" 
  proof-
    have "a[^] (Suc n) = a[^] n  a"
      by simp
    then show ?thesis using assms IH 
      using IH assms(1) assms(2) local.integral by auto
  qed
qed

lemma (in UP_cring) cring_monom_degree:
  assumes "a  (carrier R)"
  assumes "p = monom P a m"
  assumes "a[^]n  𝟬"
  shows "degree (p[^]P n) = n*m"
  by (simp add: assms(1) assms(2) assms(3) monom_pow)
  
lemma (in UP_domain) monom_degree:
  assumes "a 𝟬"
  assumes "a  (carrier R)"
  assumes "p = monom P a m"
  shows "degree (p[^]P n) = n*m"
  by (simp add: R.domain_axioms assms(1) assms(2) assms(3) domain.nonzero_pow_nonzero monom_pow)
  
lemma(in UP_cring)  cring_pow_ltrm:
  assumes "p  carrier P" 
  assumes "lcf p [^]n  𝟬"
  shows "ltrm (p[^]P(n::nat)) = (ltrm p)[^]Pn"
proof-
  have "lcf p [^]n  𝟬  ltrm (p[^]P(n::nat)) = (ltrm p)[^]Pn"
  proof(induction n)
    case 0
    then show ?case 
      using P.ring_simprules(6) P.nat_pow_0 cfs_one deg_one monom_one by presburger
  next
    case (Suc n) fix n::nat
    assume IH : "(lcf p [^] n  𝟬  ltrm (p [^]P n) = ltrm p [^]P n)"
    assume A: "lcf p [^] Suc n  𝟬"
    have a: "ltrm (p [^]P n) = ltrm p [^]P n"
      apply(cases "lcf p [^] n = 𝟬")
      using A lcf_closed assms(1) apply auto[1]
      by(rule IH)
    have 0: "lcf (ltrm (p [^]P n)) = lcf p [^] n"
      unfolding a 
      by (simp add: lcf_monom(1) assms(1) cfs_closed monom_pow)
    then have 1: "lcf (ltrm (p [^]P n))  lcf p  𝟬"
      using assms A R.nat_pow_Suc IH  by metis   
    then show "ltrm (p [^]P Suc n) = ltrm p [^]P Suc n"
      using IH 0 assms(1) cring_ltrm_mult cfs_closed 
      by (smt A lcf_monom(1) ltrm_closed P.nat_pow_Suc2 P.nat_pow_closed R.nat_pow_Suc2 a)    
  qed
  then show ?thesis 
    using assms(2) by blast
qed

lemma(in UP_cring)  cring_pow_deg:
  assumes "p  carrier P" 
  assumes "lcf p [^]n  𝟬"
  shows "degree (p[^]P(n::nat)) = n*degree p"
proof-
  have "degree ( (ltrm p)[^]Pn) = n*degree p"
    using assms(1) assms(2) cring_monom_degree lcf_closed lcf_ltrm by auto
  then show ?thesis 
    using assms cring_pow_ltrm 
    by (metis P.nat_pow_closed P_def UP_ring.deg_ltrm UP_ring_axioms)
qed

lemma(in UP_cring)  cring_pow_deg_bound:
  assumes "p  carrier P" 
  shows "degree (p[^]P(n::nat))  n*degree p"
  apply(induction n)
   apply (metis Group.nat_pow_0 deg_one le_zero_eq mult_is_0)  
  using deg_mult_ring[of _ p]
  by (smt P.nat_pow_Suc2 P.nat_pow_closed ab_semigroup_add_class.add_ac(1) assms deg_mult_ring le_iff_add mult_Suc)  

lemma(in UP_cring) deg_smult:
  assumes "a  carrier R"
  assumes "f  carrier (UP R)"
  assumes "a  lcf f  𝟬"
  shows "deg R (a UP R f) = deg R  f"
  using assms  P_def cfs_smult deg_eqI deg_smult_decr smult_closed 
  by (metis deg_gtE le_neq_implies_less)
 
lemma(in UP_cring) deg_smult':
  assumes "a  Units R"
  assumes "f  carrier (UP R)"
  shows "deg R (a UP R f) = deg R  f"
  apply(cases "deg R f = 0")
  apply (metis P_def R.Units_closed assms(1) assms(2) deg_smult_decr le_zero_eq)
  apply(rule deg_smult) 
  using assms apply blast
  using assms apply blast
proof
  assume A: "deg R f  0" "a  f (deg R f) = 𝟬"
  have 0: "f (deg R f) = 𝟬"
    using A assms R.Units_not_right_zero_divisor[of a "f (deg R f)"] UP_car_memE(1) by blast
  then show False  using assms A 
    by (metis P_def deg_zero deg_ltrm monom_zero)
qed

lemma(in UP_domain)  pow_sum0:
" p q. p  carrier P  q  carrier P  degree q < degree p  degree ((p P q )[^]Pn) = (degree p)*n"
proof(induction n)
  case 0
  then show ?case 
    by (metis Group.nat_pow_0 deg_one mult_is_0)
next
    case (Suc n)
    fix n
    assume IH: " p q. p  carrier P  q  carrier P  
              degree q < degree p  degree ((p P q )[^]Pn) = (degree p)*n"
    then show " p q. p  carrier P  q  carrier P  
             degree q < degree p  degree ((p P q )[^]P(Suc n)) = (degree p)*(Suc n)"
    proof-
      fix p q
      assume A0: "p  carrier P" and 
           A1: "q  carrier P" and 
           A2:  "degree q < degree p"
      show "degree ((p P q )[^]P(Suc n)) = (degree p)*(Suc n)"
      proof(cases "q = 𝟬P")
        case True
        then show ?thesis 
        by (metis A0 A1 A2 IH P.nat_pow_Suc2 P.nat_pow_closed P.r_zero deg_mult 
            domain.nonzero_pow_nonzero local.domain_axioms mult_Suc_right nat_neq_iff)
      next
        case False
        then show ?thesis 
        proof-
          have P0: "degree ((p P q )[^]Pn) = (degree p)*n" 
          using A0 A1 A2 IH by auto 
          have P1: "(p P q )[^]P(Suc n) = ((p P q )[^]Pn) P (p P q )"
            by simp
          then have P2: "(p P q )[^]P(Suc n) = (((p P q )[^]Pn) P p) P (((p P q )[^]Pn) P q)"
            by (simp add: A0 A1 UP_r_distr)
          have P3: "degree (((p P q )[^]Pn) P p) = (degree p)*n + (degree p)" 
            using P0 A0 A1 A2 deg_nzero_nzero  degree_of_sum_diff_degree local.nonzero_pow_nonzero by auto
          have P4: "degree (((p P q )[^]Pn) P q) = (degree p)*n + (degree q)" 
            using P0 A0 A1 A2 deg_nzero_nzero  degree_of_sum_diff_degree local.nonzero_pow_nonzero False deg_mult 
            by simp
          have P5: "degree (((p P q )[^]Pn) P p) > degree (((p P q )[^]Pn) P q)"
            using P3 P4 A2 by auto 
          then show ?thesis using P5 P3 P2 
            by (simp add: A0 A1 degree_of_sum_diff_degree)
        qed
      qed
    qed
qed

lemma(in UP_domain)  pow_sum:
  assumes "p  carrier P" 
  assumes "q  carrier P"
  assumes "degree q < degree p"
  shows "degree ((p P q )[^]Pn) = (degree p)*n"
  using assms(1) assms(2) assms(3) pow_sum0 by blast

lemma(in UP_domain)  deg_pow0:
 " p. p  carrier P  n  degree p  degree (p [^]P m) = m*(degree p)"
proof(induction n)
  case 0
  show "p  carrier P  0  degree p  degree (p [^]P m) = m*(degree p)"
  proof-
    assume B0:"p  carrier P"
    assume B1: "0  degree p"
    then obtain a where a_def: "a  carrier R  p = monom P a 0"
      using B0 deg_zero_impl_monom  by fastforce
    show "degree (p [^]P m) = m*(degree p)"  using UP_cring.monom_pow 
      by (metis P_def R.nat_pow_closed UP_cring_axioms a_def deg_const  
        mult_0_right mult_zero_left)
  qed
next
  case (Suc n)
  fix n
  assume IH: "p. (p  carrier P  n degree p  degree (p [^]P m) = m * (degree p))"
  show "p  carrier P  Suc n  degree p  degree (p [^]P m) = m * (degree p)"
  proof-
    assume A0: "p  carrier P"
    assume A1: "Suc n  degree p"
    show "degree (p [^]P m) = m * (degree p)"
    proof(cases "Suc n > degree p")
      case True
      then show ?thesis using IH A0 by simp
    next
      case False
      then show ?thesis 
      proof-
        obtain q where q_def: "q = trunc p"
          by simp
        obtain k where k_def: "k = degree q"
          by simp
        have q_is_poly: "q  carrier P" 
          by (simp add: A0 q_def trunc_closed)
        have k_bound0: "k <degree p" 
          using k_def q_def trunc_degree[of p] A0 False by auto
        have k_bound1: "k  n" 
          using k_bound0 A0 A1 by auto  
        have P_q:"degree (q [^]P m) = m * k" 
          using IH[of "q"] k_bound1 k_def q_is_poly by auto  
        have P_ltrm: "degree ((ltrm p) [^]P m) = m*(degree p)"
        proof-
          have "degree p = degree (ltrm p)" 
            by (simp add: A0 deg_ltrm)            
          then show ?thesis using monom_degree 
            by (metis A0 P.r_zero P_def cfs_closed coeff_simp equal_deg_sum k_bound0 k_def lcoeff_nonzero2 nat_neq_iff q_is_poly)            
        qed
        have "p = q P (ltrm p)" 
          by (simp add: A0 q_def trunc_simps(1))
        then show ?thesis 
          using P_q pow_sum[of "(ltrm p)" q m] A0 UP_a_comm 
            deg_ltrm k_bound0 k_def ltrm_closed q_is_poly by auto
      qed
    qed
  qed
qed

lemma(in UP_domain)  deg_pow:
  assumes "p  carrier P"
  shows "degree (p [^]P m) = m*(degree p)"
  using deg_pow0 assms by blast

lemma(in UP_domain)  ltrm_pow0:
"f. f  carrier P  ltrm (f [^]P (n::nat)) = (ltrm f) [^]P n"
proof(induction n)
  case 0
  then show ?case 
    using ltrm_deg_0 P.nat_pow_0 P.ring_simprules(6) deg_one by presburger  
next
  case (Suc n)
  fix n::nat
  assume IH: "f. f  carrier P  ltrm (f [^]P n) = (ltrm f) [^]P n"
  then show "f. f  carrier P  ltrm (f [^]P (Suc n)) = (ltrm f) [^]P (Suc n)"
  proof-
    fix f
    assume A: "f  carrier P"
    show " ltrm (f [^]P (Suc n)) = (ltrm f) [^]P (Suc n)"
    proof-
      have 0: "ltrm (f [^]P n) = (ltrm f) [^]P n" 
        using A IH  by blast
      have 1: "ltrm (f [^]P (Suc n)) = ltrm ((f [^]P n)P f)" 
        by auto then 
      show ?thesis using ltrm_mult 0 1 
        by (simp add: A)
    qed
  qed
qed

lemma(in UP_domain)  ltrm_pow:
  assumes "f  carrier P"
  shows " ltrm (f [^]P (n::nat)) = (ltrm f) [^]P n"
  using assms ltrm_pow0 by blast

text‹lemma on the leading coefficient›

lemma lcf_eq:
  assumes "f  carrier P"
  shows "lcf f = lcf (ltrm f)"
  using ltrm_deg_0 
  by (simp add: ltrm_cfs assms deg_ltrm)
  
lemma lcf_eq_deg_eq_imp_ltrm_eq:
  assumes "p  carrier P"
  assumes "q  carrier P"
  assumes "degree p > 0"
  assumes "degree p = degree q"
  assumes "lcf p = lcf q"
  shows "ltrm p = ltrm q"
  using assms(4) assms(5) 
  by (simp add: leading_term_def)
  
lemma ltrm_eq_imp_lcf_eq:
  assumes "p  carrier P"
  assumes "q  carrier P"
  assumes "ltrm p = ltrm q"
  shows "lcf p = lcf q"
  using assms 
  by (metis lcf_eq)

lemma ltrm_eq_imp_deg_drop:
  assumes "p  carrier P"
  assumes "q  carrier P"
  assumes "ltrm p = ltrm q"
  assumes "degree p >0"
  shows "degree (p P q) < degree p"
proof-
  have P0: "degree p = degree q"
    by (metis assms(1) assms(2) assms(3) deg_ltrm)
  then have P1: "degree (p P q)  degree p"
    by (metis P.add.inv_solve_right P.minus_closed P.minus_eq assms(1)
        assms(2) degree_of_sum_diff_degree neqE order.strict_implies_order order_refl)
  have "degree (p P q)  degree p"
  proof
    assume A: "degree (p P q) = degree p"
    have Q0: "p P q = ((trunc p) P (ltrm p)) P ((trunc q) P (ltrm p))"
      using assms(1) assms(2) assms(3) trunc_simps(1) by force
    have Q1: "p P q = (trunc p)  P (trunc q)" 
    proof-
      have "p P q = ((trunc p) P (ltrm p)) P (trunc q)  P (ltrm p)"
        using Q0 
        by (simp add: P.minus_add P.minus_eq UP_a_assoc assms(1) assms(2) ltrm_closed trunc_closed)
      then show ?thesis 
        by (metis (no_types, lifting) ltrm_closed P.add.inv_mult_group P.minus_eq 
            P.r_neg2 UP_a_assoc assms(1) assms(2) assms(3) carrier_is_submodule submoduleE(6) trunc_closed trunc_simps(1))        
    qed
    have Q2: "degree (trunc p) < degree p" 
      by (simp add: assms(1) assms(4) trunc_degree)
    have Q3: "degree (trunc q) < degree q" 
      using P0 assms(2) assms(4) trunc_degree by auto
    then show False  using A Q1 Q2 Q3 by (simp add: P.add.inv_solve_right
          P.minus_eq P0 assms(1) assms(2) degree_of_sum_diff_degree trunc_closed)
  qed
  then show ?thesis 
    using P1 by auto
qed

lemma(in UP_cring) cring_lcf_scalar_mult:
  assumes "p  carrier P"
  assumes "a  carrier R"
  assumes "a  (lcf p) 𝟬"
  shows "lcf (a P p) = a  (lcf p)"
proof-
  have 0: "lcf (a P p) = lcf (ltrm (a P p))"
    using assms  lcf_eq smult_closed by blast
  have 1: "degree (a P p) = degree p"
    by (smt lcf_monom(1) P_def R.one_closed R.r_null UP_ring.coeff_smult UP_ring_axioms  
        assms(1) assms(2) assms(3) coeff_simp cring_deg_mult deg_const monom_closed monom_mult_is_smult smult_one)
  then have "lcf (a P p) = lcf (a P (ltrm p))"
    using lcf_eq[of "a P p"] smult_closed  assms 0 
    by (metis cfs_closed cfs_smult monom_mult_smult)     
  then show ?thesis 
    unfolding leading_term_def 
    by (metis P_def R.m_closed UP_cring.lcf_monom UP_cring_axioms assms(1) assms(2) cfs_closed monom_mult_smult)   
qed

lemma(in UP_domain) lcf_scalar_mult:
  assumes "p  carrier P"
  assumes "a  carrier R"
  shows "lcf (a P p) = a  (lcf p)"
proof-
  have "lcf (a P p) = lcf (ltrm (a P p))"
    using lcf_eq UP_smult_closed assms(1) assms(2) by blast 
  then have "lcf (a P p) = lcf (a P (ltrm p))"
    using ltrm_smult assms(1) assms(2) by metis   
  then show ?thesis 
    by (metis (full_types) UP_smult_zero assms(1) assms(2) cfs_smult cfs_zero deg_smult)    
qed

lemma(in UP_cring)  cring_lcf_mult:
  assumes "p  carrier P"
  assumes "q  carrier P"
  assumes "(lcf p)  (lcf q) 𝟬"
  shows "lcf (p P q) = (lcf p)  (lcf q)"
  using assms cring_ltrm_mult 
  by (smt lcf_monom(1) P.m_closed R.m_closed cfs_closed monom_mult)

lemma(in UP_domain)  lcf_mult:
  assumes "p  carrier P"
  assumes "q  carrier P"
  shows "lcf (p P q) = (lcf p)  (lcf q)"
  by (smt ltrm_deg_0 R.integral_iff assms(1) assms(2) cfs_closed cring_lcf_mult deg_zero deg_ltrm local.integral_iff monom_zero)

lemma(in UP_cring)  cring_lcf_pow:
  assumes "p  carrier P"
  assumes "(lcf p)[^]n 𝟬"
  shows "lcf (p[^]P(n::nat)) = (lcf p)[^]n"
  by (smt P.nat_pow_closed R.nat_pow_closed assms(1) assms(2) cring_pow_ltrm lcf_closed lcf_ltrm lcf_monom monom_pow)

lemma(in UP_domain)  lcf_pow:
  assumes "p  carrier P"
  shows "lcf (p[^]P(n::nat)) = (lcf p)[^]n"
proof-
  show ?thesis 
  proof(induction n)
    case 0
    then show ?case 
      by (metis Group.nat_pow_0 P_def R.one_closed UP_cring.lcf_monom UP_cring_axioms monom_one)    
  next
    case (Suc n)
    fix n
    assume IH: "lcf (p[^]P(n::nat)) = (lcf p)[^]n"
    show "lcf (p[^]P(Suc n)) = (lcf p)[^](Suc n)"
    proof-
      have "lcf (p[^]P(Suc n)) = lcf ((p[^]Pn) Pp)"
        by simp
      then have "lcf (p[^]P(Suc n)) = (lcf p)[^]n  (lcf p)"
        by (simp add: IH assms lcf_mult)
      then show ?thesis by auto 
    qed
  qed
qed
end

  (**************************************************************************************************)
  (**************************************************************************************************)
  subsection‹Constant Terms and Constant Coefficients›
  (**************************************************************************************************)
  (**************************************************************************************************)

text‹Constant term  and coefficient function›

definition zcf where
"zcf f = (f 0)"

abbreviation(in UP_cring)(input) ctrm where
"ctrm f  monom P (f 0) 0"

context UP_cring
begin

lemma ctrm_is_poly:
  assumes "p  carrier P"
  shows "ctrm p  carrier P"
  by (simp add: assms cfs_closed)

lemma ctrm_degree:
  assumes "p  carrier P"
  shows "degree (ctrm p) = 0"
  by (simp add: assms cfs_closed) 
  
lemma ctrm_zcf:
assumes "f  carrier P"
assumes "zcf f = 𝟬"
shows "ctrm f = 𝟬P"
  by (metis P_def UP_ring.monom_zero UP_ring_axioms zcf_def assms(2))

lemma zcf_degree_zero:
  assumes "f  carrier P"
  assumes "degree f = 0"
  shows "lcf f = zcf f"
  by (simp add: zcf_def assms(2))
  
lemma zcf_zero_degree_zero:
  assumes "f  carrier P"
  assumes "degree f = 0"
  assumes "zcf f = 𝟬"
  shows "f = 𝟬P"
  using zcf_degree_zero[of f] assms ltrm_deg_0[of f] 
  by simp

lemma zcf_ctrm:
  assumes "p  carrier P"
  shows "zcf (ctrm p) = zcf p"
  unfolding zcf_def 
  using P_def UP_ring.cfs_monom UP_ring_axioms assms cfs_closed by fastforce
 
lemma ctrm_trunc:
  assumes "p  carrier P"
  assumes "degree p >0"
  shows "zcf(trunc p) = zcf p"
  by (simp add: zcf_def assms(1) assms(2) trunc_cfs)

text‹Constant coefficient function is a ring homomorphism›


lemma zcf_add:
  assumes "p  carrier P"
  assumes "q  carrier P"
  shows "zcf(p P q) = (zcf p)  (zcf q)"
  by (simp add: zcf_def assms(1) assms(2))

lemma coeff_ltrm[simp]:
  assumes "p  carrier P"
  assumes "degree p > 0"
  shows "zcf(ltrm p) = 𝟬"
  by (metis ltrm_cfs_above_deg ltrm_cfs zcf_def assms(1) assms(2))

lemma zcf_zero[simp]:
"zcf 𝟬P = 𝟬"
  using zcf_degree_zero by auto

lemma zcf_one[simp]:
"zcf 𝟭P = 𝟭"
  by (simp add: zcf_def)

lemma ctrm_smult:
  assumes "f  carrier P"
  assumes "a   carrier R"
  shows "ctrm (a P f) = a P(ctrm f)"
  using P_def UP_ring.monom_mult_smult UP_ring_axioms assms(1) assms(2) cfs_smult coeff_simp 
  by (simp add: UP_ring.monom_mult_smult cfs_closed)

lemma ctrm_monom[simp]:
  assumes "a  carrier R"
  shows "ctrm (monom P a (Suc k)) = 𝟬P"
  by (simp add: assms cfs_monom)
end
  (**************************************************************************************************)
  (**************************************************************************************************)
  subsection‹Polynomial Induction Rules›
  (**************************************************************************************************)
  (**************************************************************************************************)

context UP_ring
begin

text‹Rule for strong induction on polynomial degree›

lemma poly_induct:
  assumes "p  carrier P"
  assumes Deg_0: "p. p  carrier P  degree p = 0  Q p"
  assumes IH: "p. (q. q  carrier P  degree q < degree p  Q q)  p  carrier P  degree p > 0  Q p"
  shows "Q p"
proof-
  have "n. p. p  carrier P  degree p  n  Q p"
  proof-
    fix n
    show "p. p  carrier P   degree p  n  Q p"
    proof(induction n)
      case 0
      then show ?case 
        using Deg_0  by simp
    next
      case (Suc n)
      fix n 
      assume I:  "p. p  carrier P   degree p  n  Q p"
      show  "p. p  carrier P   degree p  (Suc n)  Q p"
      proof-
        fix p
        assume A0: " p  carrier P "
        assume A1: "degree p Suc n"
        show "Q p"
        proof(cases "degree p < Suc n")
          case True
          then show ?thesis 
            using I  A0 by auto
        next
          case False
          then have D: "degree p = Suc n" 
            by (simp add: A1 nat_less_le)
          then  have "(q. q  carrier P  degree q < degree p  Q q)" 
              using I   by simp
            then show "Q p" 
                  using IH D A0 A1 Deg_0 by blast
        qed
      qed
    qed
  qed
  then show ?thesis using assms by blast 
qed

text‹Variant on induction on degree›

lemma poly_induct2:
  assumes "p  carrier P"
  assumes Deg_0: "p. p  carrier P  degree p = 0  Q p"
  assumes IH: "p. degree p > 0   p  carrier P  Q (trunc p)   Q p"
  shows "Q p"
proof(rule poly_induct)
  show "p  carrier P" 
    by (simp add: assms(1))
  show "p. p  carrier P  degree p = 0  Q p" 
      by (simp add: Deg_0)
    show "p. (q. q  carrier P  degree q < degree p  Q q)  p  carrier P  0 < degree p  Q p"
    proof-
      fix p
      assume A0: "(q. q  carrier P  degree q < degree p  Q q)"
      assume A1: " p  carrier P"
      assume A2: "0 < degree p" 
      show "Q p"
      proof-
        have "degree (trunc p) < degree p"
          by (simp add: A1 A2 trunc_degree)
        have "Q (trunc p)"
          by (simp add: A0 A1 ‹degree (trunc p) < degree p trunc_closed)
        then show ?thesis 
          by (simp add: A1 A2 IH)
      qed
    qed
qed

text‹Additive properties which are true for all monomials are true for all polynomials ›

lemma poly_induct3:
  assumes "p  carrier P"
  assumes add: "p q. q  carrier P  p  carrier P  Q p  Q q   Q (p P q)"
  assumes monom: "a n. a  carrier R  Q (monom P a n)"
  shows "Q p"
  apply(rule poly_induct2)
  apply (simp add: assms(1))
  apply (metis lcf_closed P_def coeff_simp deg_zero_impl_monom monom)
  by (metis lcf_closed ltrm_closed add monom trunc_closed trunc_simps(1))

lemma poly_induct4:
  assumes "p  carrier P"
  assumes add: "p q. q  carrier P  p  carrier P  Q p  Q q   Q (p P q)"
  assumes monom_zero: "a. a  carrier R  Q (monom P a 0)"
  assumes monom_Suc: "a n. a  carrier R  Q (monom P a (Suc n))"
  shows "Q p"
  apply(rule poly_induct3)
  using assms(1) apply auto[1]
  using add apply blast
  using monom_zero monom_Suc 
  by (metis P_def UP_ring.monom_zero UP_ring_axioms deg_monom deg_monom_le le_0_eq le_SucE zero_induct)

lemma monic_monom_smult:
  assumes "a  carrier R"
  shows "a P monom P 𝟭 n = monom P a n"
  using assms 
  by (metis R.one_closed R.r_one monom_mult_smult)

lemma poly_induct5:
  assumes "p  carrier P"
  assumes add: "p q. q  carrier P  p  carrier P  Q p  Q q   Q (p P q)"
  assumes monic_monom: "n. Q (monom P 𝟭 n)"
  assumes smult: "p a . a  carrier R  p  carrier P  Q p  Q (a P p)"
  shows "Q p"
  apply(rule poly_induct3)
  apply (simp add: assms(1))
  using add apply blast
proof-
  fix a n assume A: "a  carrier R" show "Q (monom P a n)"
    using monic_monom[of n] smult[of a "monom P 𝟭 n"] monom_mult_smult[of a 𝟭 n]
  by (simp add: A)
qed

lemma poly_induct6:
  assumes "p  carrier P"
  assumes monom: "a n. a  carrier R  Q (monom P a 0)"
  assumes plus_monom: "a n p. a  carrier R  a  𝟬  p  carrier P  degree p < n  Q p 
                                Q(p P monom P a n)"
  shows "Q p"
  apply(rule poly_induct2)
  using assms(1) apply auto[1]
  apply (metis lcf_closed P_def coeff_simp deg_zero_impl_monom monom)
  using plus_monom 
  by (metis lcf_closed P_def coeff_simp lcoeff_nonzero_deg nat_less_le trunc_closed trunc_degree trunc_simps(1))
  

end

(**************************************************************************************************)
(**************************************************************************************************)
section‹Mapping a Polynomial to its Associated Ring Function›
(**************************************************************************************************)
(**************************************************************************************************)


text‹Turning a polynomial into a function on R:›
definition to_function  where
"to_function S f  = (λs  carrier S. eval S S (λx. x) s f)"

context UP_cring
begin

definition to_fun where
"to_fun f  to_function R f"

text‹Explicit formula for evaluating a polynomial function:›

lemma to_fun_eval:
  assumes "f  carrier P"
  assumes "x  carrier R"
  shows "to_fun f x  = eval R R (λx. x) x f"
  using assms unfolding to_function_def to_fun_def 
  by auto 

lemma to_fun_formula:
  assumes "f  carrier P"
  assumes "x  carrier R"
  shows "to_fun f x = (i  {..degree f}. (f i)  x [^] i)"
proof-
  have "f  carrier (UP R)"
    using assms P_def by auto 
  then  have "eval R R (λx. x) x f =  (Ri{..deg R f}. (λx. x) (coeff (UP R) f i) R x [^]R i)"
    apply(simp add:UnivPoly.eval_def) done
  then have "to_fun f x = (Ri{..deg R f}. (λx. x) (coeff (UP R) f i) R x [^]R i)"
    using to_function_def assms unfolding to_fun_def  
    by (simp add: to_function_def)
  then show ?thesis  
    by(simp add: assms coeff_simp) 
qed

lemma eval_ring_hom:
  assumes "a  carrier R"
  shows "eval R R (λx. x) a  ring_hom P R"
proof-
  have "(λx. x)  ring_hom R R"
    apply(rule ring_hom_memI)
    apply auto done
  then have "UP_pre_univ_prop R R (λx. x)"
    using R_cring UP_pre_univ_propI by blast
  then show ?thesis 
    by (simp add: P_def UP_pre_univ_prop.eval_ring_hom assms)
qed

lemma to_fun_closed:
  assumes "f  carrier P"
  assumes "x  carrier R"
  shows "to_fun f x   carrier R"
  using assms to_fun_eval[of f x] eval_ring_hom[of x]
  ring_hom_closed 
  by fastforce
  
lemma to_fun_plus:
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "x  carrier R"
  shows "to_fun (f P g) x = (to_fun f x)  (to_fun g x)"
  using assms to_fun_eval[of ] eval_ring_hom[of x]
  by (simp add: ring_hom_add)
  
lemma to_fun_mult:
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "x  carrier R"
  shows "to_fun (f P g) x = (to_fun f x)  (to_fun g x)"
  using assms to_fun_eval[of ] eval_ring_hom[of x]
  by (simp add: ring_hom_mult)

lemma to_fun_ring_hom:
  assumes "a  carrier R"
  shows "(λp. to_fun p a)  ring_hom P R"
  apply(rule ring_hom_memI)
  apply (simp add: assms to_fun_closed)
    apply (simp add: assms to_fun_mult)
      apply (simp add: assms to_fun_plus)
        using to_fun_eval[of "𝟭P" a] eval_ring_hom[of a] 
              ring_hom_closed 
        by (simp add: assms ring_hom_one)

lemma ring_hom_uminus:
  assumes "ring S"
  assumes "f  (ring_hom S R)"
  assumes "a  carrier S"
  shows "f (S a) =  (f a)"
proof-
  have "f (a S a) = (f a)  f (S a)"
    unfolding a_minus_def 
    by (simp add: assms(1) assms(2) assms(3) ring.ring_simprules(3) ring_hom_add)
  then have "(f a)  f (S a) = 𝟬 "
    by (metis R.ring_axioms a_minus_def assms(1) assms(2) assms(3) 
        ring.ring_simprules(16) ring_hom_zero)
  then show ?thesis 
    by (metis (no_types, lifting) R.add.m_comm R.minus_equality assms(1)
        assms(2) assms(3) ring.ring_simprules(3) ring_hom_closed)
qed

lemma to_fun_minus:
  assumes "f  carrier P"
  assumes "x  carrier R"
  shows "to_fun (Pf) x =  (to_fun f x)"
  unfolding to_function_def to_fun_def 
  using  eval_ring_hom[of x] assms  
  by (simp add: UP_ring ring_hom_uminus)

lemma id_is_hom:
"ring_hom_cring R R (λx. x)"
proof(rule ring_hom_cringI)
  show "cring R" 
    by (simp add: R_cring )
  show "cring R" 
    by (simp add: R_cring ) 
  show "(λx. x)  ring_hom R R"
    unfolding ring_hom_def
    apply(auto)
    done
qed

lemma UP_pre_univ_prop_fact:
"UP_pre_univ_prop R R (λx. x)"
  unfolding UP_pre_univ_prop_def
  by (simp add: UP_cring_def R_cring  id_is_hom)

end

  (**************************************************************************************************)
  (**************************************************************************************************)
  subsection‹to-fun is a Ring Homomorphism from Polynomials to Functions›
  (**************************************************************************************************)
  (**************************************************************************************************)

context UP_cring
begin

lemma to_fun_is_Fun:
  assumes "x  carrier P"
  shows "to_fun x  carrier (Fun R)"
  apply(rule ring_functions.function_ring_car_memI)
  unfolding ring_functions_def apply(simp add: R.ring_axioms)
  using to_fun_closed assms apply auto[1]
  unfolding to_function_def to_fun_def  by auto 

lemma to_fun_Fun_mult:
  assumes "x  carrier P"
  assumes "y  carrier P"
  shows "to_fun (x P y) = to_fun x function_ring (carrier R) R to_fun y"
  apply(rule ring_functions.function_ring_car_eqI[of R _ "carrier R"])
  apply (simp add: R.ring_axioms ring_functions_def)
    apply (simp add: assms(1) assms(2) to_fun_is_Fun)
      apply (simp add: R.ring_axioms assms(1) assms(2) ring_functions.fun_mult_closed ring_functions.intro to_fun_is_Fun)
        by (simp add: R.ring_axioms assms(1) assms(2) ring_functions.function_mult_eval_car ring_functions.intro to_fun_is_Fun to_fun_mult)

lemma to_fun_Fun_add:
  assumes "x  carrier P"
  assumes "y  carrier P"
  shows "to_fun (x P y) = to_fun x function_ring (carrier R) R to_fun y"
  apply(rule ring_functions.function_ring_car_eqI[of R _ "carrier R"])
  apply (simp add: R.ring_axioms ring_functions_def)
    apply (simp add: assms(1) assms(2) to_fun_is_Fun)
      apply (simp add: R.ring_axioms assms(1) assms(2) ring_functions.fun_add_closed ring_functions.intro to_fun_is_Fun)
        by (simp add: R.ring_axioms assms(1) assms(2) ring_functions.fun_add_eval_car ring_functions.intro to_fun_is_Fun to_fun_plus)

lemma to_fun_Fun_one:
"to_fun 𝟭P = 𝟭Fun R"
  apply(rule ring_functions.function_ring_car_eqI[of R _ "carrier R"])
  apply (simp add: R.ring_axioms ring_functions_def)
    apply (simp add: to_fun_is_Fun)
      apply (simp add: R.ring_axioms ring_functions.function_one_closed ring_functions_def)
        using P_def R.ring_axioms UP_cring.eval_ring_hom UP_cring.to_fun_eval UP_cring_axioms UP_one_closed ring_functions.function_one_eval ring_functions.intro ring_hom_one 
          by fastforce

lemma to_fun_Fun_zero:
"to_fun 𝟬P = 𝟬Fun R"
  apply(rule ring_functions.function_ring_car_eqI[of R _ "carrier R"])
  apply (simp add: R.ring_axioms ring_functions_def)
    apply (simp add: to_fun_is_Fun)
      apply (simp add: R.ring_axioms ring_functions.function_zero_closed ring_functions_def)
        using P_def R.ring_axioms UP_cring.eval_ring_hom UP_cring.to_fun_eval UP_cring_axioms UP_zero_closed ring_functions.function_zero_eval ring_functions.intro ring_hom_zero 
        by (metis UP_ring eval_ring_hom)

lemma to_fun_function_ring_hom:
"to_fun  ring_hom P (Fun R)"
  apply(rule ring_hom_memI)
    using to_fun_is_Fun apply auto[1]
      apply (simp add: to_fun_Fun_mult)
        apply (simp add: to_fun_Fun_add)
          by (simp add: to_fun_Fun_one)

lemma(in UP_cring) to_fun_one:
  assumes "a  carrier R"
  shows "to_fun 𝟭P a = 𝟭"
  using assms to_fun_Fun_one 
  by (metis P_def UP_cring.to_fun_eval UP_cring_axioms UP_one_closed eval_ring_hom ring_hom_one) 

lemma(in UP_cring) to_fun_zero:
  assumes "a  carrier R"
  shows "to_fun 𝟬P a = 𝟬"
  by (simp add: assms R.ring_axioms ring_functions.function_zero_eval ring_functions.intro to_fun_Fun_zero) 

lemma(in UP_cring) to_fun_nat_pow:
  assumes "h  carrier (UP R)"
  assumes "a  carrier R"
  shows "to_fun (h[^]UP R(n::nat)) a = (to_fun h a)[^]n"
  apply(induction n)
  using assms to_fun_one 
  apply (metis P.nat_pow_0 P_def R.nat_pow_0)
  using assms to_fun_mult  P.nat_pow_closed P_def by auto

lemma(in UP_cring) to_fun_finsum: 
  assumes "finite (Y::'d set)"
  assumes "f  UNIV  carrier (UP R)"
  assumes "t  carrier R"
  shows "to_fun (finsum (UP R) f Y) t = finsum R (λi. (to_fun (f i) t)) Y"
proof(rule finite.induct[of Y])
  show "finite Y"
    using assms by blast 
  show "to_fun (finsum (UP R) f {}) t = (i{}. to_fun (f i) t)"
    using P.finsum_empty[of f] assms unfolding P_def R.finsum_empty 
    using P_def to_fun_zero by presburger
  show  "A a. finite A 
           to_fun (finsum (UP R) f A) t = (iA. to_fun (f i) t)  to_fun (finsum (UP R) f (insert a A)) t = (iinsert a A. to_fun (f i) t)"
  proof-     
    fix A :: "'d set" fix a
    assume A: "finite A" "to_fun (finsum (UP R) f A) t = (iA. to_fun (f i) t)"
    show "to_fun (finsum (UP R) f (insert a A)) t = (iinsert a A. to_fun (f i) t)"
    proof(cases "a  A")
      case True
      then show ?thesis using A 
        by (metis insert_absorb)
    next
      case False
      have 0: "finsum (UP R) f (insert a A) = f a UP R finsum (UP R) f A"
        using A False finsum_insert[of A a f] assms unfolding P_def  by blast
      have 1: "to_fun (f a Pfinsum (UP R) f A ) t =  to_fun (f a) t  to_fun (finsum (UP R) f A) t"
        apply(rule to_fun_plus[of  "finsum (UP R) f A" "f a" t])
        using assms(2) finsum_closed[of f A] A unfolding P_def apply blast
        using P_def assms apply blast
        using assms by blast
      have 2: "to_fun (f a Pfinsum (UP R) f A ) t =  to_fun (f a) t  (iA. to_fun (f i) t)"
        unfolding  1 A by blast 
      have 3: "(iinsert a A. to_fun (f i) t) = to_fun (f a) t  (iA. to_fun (f i) t)"
        apply(rule  R.finsum_insert, rule A, rule False)
        using to_fun_closed assms unfolding P_def apply blast
        apply(rule to_fun_closed) using assms unfolding P_def apply blast using assms by blast 
      show ?thesis 
        unfolding 0 unfolding 3 using 2 unfolding P_def by blast 
    qed
  qed
qed

end

(**************************************************************************************************)
(**************************************************************************************************)
subsection‹Inclusion of a Ring into its Polynomials Ring via Constants›
(**************************************************************************************************)
(**************************************************************************************************)

definition to_polynomial where
"to_polynomial R = (λa. monom (UP R) a 0)"

context UP_cring
begin

abbreviation(input) to_poly where
"to_poly   to_polynomial R"

lemma to_poly_mult_simp:
  assumes "b  carrier R"
  assumes "f  carrier (UP R)"
  shows "(to_polynomial R b) UP R f = b UP R f"
        "f  UP R (to_polynomial R b) = b UP R f"   
  unfolding to_polynomial_def
  using assms  P_def monom_mult_is_smult apply auto[1]
  using UP_cring.UP_m_comm UP_cring_axioms UP_ring.monom_closed 
        UP_ring.monom_mult_is_smult UP_ring_axioms assms(1) assms(2) 
  by fastforce  

lemma to_fun_to_poly:
  assumes "a  carrier R"
  assumes "b  carrier R"
  shows "to_fun (to_poly a) b = a"
  unfolding to_function_def to_fun_def to_polynomial_def 
  by (simp add: UP_pre_univ_prop.eval_const UP_pre_univ_prop_fact assms(1) assms(2))
  
lemma to_poly_inverse:
  assumes "f  carrier P"
  assumes "degree f = 0"
  shows "f = to_poly (f 0)"
  using P_def assms(1) assms(2) 
  by (metis ltrm_deg_0 to_polynomial_def) 

lemma to_poly_closed:
  assumes "a  carrier R"
  shows "to_poly a  carrier P"
  by (metis P_def assms monom_closed to_polynomial_def)

lemma degree_to_poly[simp]:
  assumes "a  carrier R"
  shows "degree (to_poly a) = 0"
  by (metis P_def assms deg_const to_polynomial_def)

lemma to_poly_is_ring_hom:
"to_poly  ring_hom R P"
  unfolding to_polynomial_def
  unfolding P_def
  using UP_ring.const_ring_hom[of R]
  UP_ring_axioms by simp 

lemma to_poly_add:
  assumes "a  carrier R"
  assumes "b  carrier R"
  shows "to_poly (a  b) = to_poly a P to_poly b"
  by (simp add: assms(1) assms(2) ring_hom_add to_poly_is_ring_hom)

lemma to_poly_mult:
  assumes "a  carrier R"
  assumes "b  carrier R"
  shows "to_poly (a  b) = to_poly a P to_poly b"
  by (simp add: assms(1) assms(2) ring_hom_mult to_poly_is_ring_hom)

lemma to_poly_minus:
  assumes "a  carrier R"
  assumes "b  carrier R"
  shows "to_poly (a  b) = to_poly a P to_poly b"
  by (metis P.minus_eq P_def R.add.inv_closed R.ring_axioms UP_ring.monom_add 
      UP_ring_axioms assms(1) assms(2) monom_a_inv ring.ring_simprules(14) to_polynomial_def)

lemma to_poly_a_inv:
  assumes "a  carrier R"
  shows "to_poly ( a) =  P to_poly a"
  by (metis P_def assms monom_a_inv to_polynomial_def)

lemma to_poly_nat_pow:
  assumes "a  carrier R"
  shows "(to_poly a) [^]P (n::nat)= to_poly (a[^]n)"
  using assms UP_cring UP_cring_axioms UP_cring_def UnivPoly.ring_hom_cringI ring_hom_cring.hom_pow to_poly_is_ring_hom 
  by fastforce


end

(**************************************************************************************************)
(**************************************************************************************************)
section‹Polynomial Substitution›
(**************************************************************************************************)
(**************************************************************************************************)

definition compose where
"compose R f g = eval R (UP R) (to_polynomial R) g f"

abbreviation(in UP_cring)(input) sub  (infixl "of" 70) where
"sub f g  compose R f g"

definition rev_compose  where
"rev_compose R = eval R (UP R) (to_polynomial R)"

abbreviation(in UP_cring)(input) rev_sub  where
"rev_sub  rev_compose R"

context UP_cring
begin

lemma  sub_rev_sub:
"sub f g = rev_sub g f"
  unfolding compose_def rev_compose_def 
  by simp  

lemma(in UP_cring) to_poly_UP_pre_univ_prop:
"UP_pre_univ_prop R P to_poly"
proof 
  show "to_poly  ring_hom R P" 
    by (simp add: to_poly_is_ring_hom)
qed

lemma rev_sub_is_hom:
  assumes "g  carrier P"
  shows "rev_sub g  ring_hom P P"
  unfolding rev_compose_def
  using to_poly_UP_pre_univ_prop assms(1) UP_pre_univ_prop.eval_ring_hom[of R P to_poly g] 
  unfolding P_def apply auto 
  done

lemma rev_sub_closed:
  assumes "p  carrier P"
  assumes "q  carrier P"
  shows "rev_sub q p  carrier P"
  using rev_sub_is_hom[of q] assms ring_hom_closed[of "rev_sub q" P P p] by auto  

lemma sub_closed:
  assumes "p  carrier P"
  assumes "q  carrier P"
  shows "sub q p  carrier P"
  by (simp add: assms(1) assms(2) rev_sub_closed sub_rev_sub)

lemma rev_sub_add:
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "h carrier P"
  shows "rev_sub g (f P h) = (rev_sub g f) P (rev_sub g h)"
  using rev_sub_is_hom assms ring_hom_add by fastforce

lemma sub_add: 
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "h carrier P"
  shows "((f P h) of g) = ((f of g) P (h of g))"
  by (simp add: assms(1) assms(2) assms(3) rev_sub_add sub_rev_sub)

lemma rev_sub_mult:
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "h carrier P"
  shows "rev_sub g (f P h) = (rev_sub g f) P (rev_sub g h)"
  using rev_sub_is_hom assms ring_hom_mult  by fastforce

lemma sub_mult: 
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "h carrier P"
  shows "((f P h) of g) = ((f of g) P (h of g))"
  by (simp add: assms(1) assms(2) assms(3) rev_sub_mult sub_rev_sub)

lemma sub_monom:
  assumes "g  carrier (UP R)"
  assumes "a  carrier R"
  shows "sub (monom (UP R) a n) g = to_poly a  UP R (g[^]UP R (n::nat))" 
        "sub (monom (UP R) a n) g = a UP R (g[^]UP R (n::nat))" 
   apply (simp add: UP_cring.to_poly_UP_pre_univ_prop UP_cring_axioms 
          UP_pre_univ_prop.eval_monom assms(1) assms(2) Cring_Poly.compose_def)
  by (metis P_def UP_cring.to_poly_mult_simp(1) UP_cring_axioms UP_pre_univ_prop.eval_monom 
      UP_ring assms(1) assms(2) Cring_Poly.compose_def monoid.nat_pow_closed ring_def to_poly_UP_pre_univ_prop) 
 
text‹Subbing into a constant does nothing›

lemma rev_sub_to_poly:
  assumes "g  carrier P"
  assumes "a  carrier R"
  shows "rev_sub g (to_poly a) = to_poly a"
  unfolding to_polynomial_def rev_compose_def
  using to_poly_UP_pre_univ_prop 
  unfolding to_polynomial_def 
     using P_def UP_pre_univ_prop.eval_const assms(1) assms(2) by fastforce

lemma sub_to_poly:
  assumes "g  carrier P"
  assumes "a  carrier R"
  shows "(to_poly a) of g  = to_poly a"
  by (simp add: assms(1) assms(2) rev_sub_to_poly sub_rev_sub)

lemma sub_const:
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "degree f = 0"
  shows "f of g = f"
  by (metis lcf_closed assms(1) assms(2) assms(3) sub_to_poly to_poly_inverse)

text‹Substitution into a monomial›

lemma monom_sub:
  assumes "a  carrier R"
  assumes "g  carrier P"
  shows "(monom P a n) of g = a P g[^]P n"
    unfolding compose_def
    using assms UP_pre_univ_prop.eval_monom[of R P to_poly a g n] to_poly_UP_pre_univ_prop 
    unfolding P_def  
    using P.nat_pow_closed P_def  to_poly_mult_simp(1) 
    by (simp add: to_poly_mult_simp(1) UP_cring_axioms)
    
lemma(in UP_cring)  cring_sub_monom_bound:
  assumes "a  carrier R"
  assumes "a 𝟬"
  assumes "f = monom P a n"
  assumes "g  carrier P"
  shows "degree (f of g)  n*(degree g)"
proof-
  have "f of g = (to_poly a) P (g[^]Pn)"
    unfolding compose_def
    using assms UP_pre_univ_prop.eval_monom[of R P to_poly a g] to_poly_UP_pre_univ_prop 
    unfolding P_def  
    by blast
  then show ?thesis 
    by (smt P.nat_pow_closed assms(1) assms(4) cring_pow_deg_bound deg_mult_ring
        degree_to_poly le_trans plus_nat.add_0 to_poly_closed)
qed

lemma(in UP_cring)  cring_sub_monom:
  assumes "a  carrier R"
  assumes "a 𝟬"
  assumes "f = monom P a n"
  assumes "g  carrier P"
  assumes "a  (lcf g [^] n)  𝟬"
  shows "degree (f of g) = n*(degree g)"
proof-
  have 0: "f of g = (to_poly a) P (g[^]Pn)"
    unfolding compose_def
    using assms UP_pre_univ_prop.eval_monom[of R P to_poly a g] to_poly_UP_pre_univ_prop 
    unfolding P_def  
    by blast
  have 1: "lcf (to_poly a)  lcf (g [^]P n)  𝟬" 
    using assms 
    by (smt P.nat_pow_closed P_def R.nat_pow_closed R.r_null cring_pow_ltrm lcf_closed lcf_ltrm lcf_monom monom_pow to_polynomial_def)
  then show ?thesis 
    using 0 1 assms cring_pow_deg[of g n] cring_deg_mult[of "to_poly a" "g[^]Pn"]
    by (metis P.nat_pow_closed R.r_null add.right_neutral degree_to_poly to_poly_closed)
qed
    
lemma(in UP_domain)  sub_monom:
  assumes "a  carrier R"
  assumes "a 𝟬"
  assumes "f = monom P a n"
  assumes "g  carrier P"
  shows "degree (f of g) = n*(degree g)"
proof-
  have "f of g = (to_poly a) P (g[^]Pn)"
    unfolding compose_def
    using assms UP_pre_univ_prop.eval_monom[of R P to_poly a g] to_poly_UP_pre_univ_prop 
    unfolding P_def  
    by blast
  then show ?thesis using deg_pow deg_mult 
    by (metis P.nat_pow_closed P_def assms(1) assms(2) 
        assms(4) deg_smult monom_mult_is_smult to_polynomial_def)
qed

text‹Subbing a constant into a polynomial yields a constant›
lemma sub_in_const:
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "degree g = 0"
  shows "degree (f of g) = 0"
proof-
  have "n. (p. p  carrier P  degree p  n  degree (p of g) = 0)"
  proof-
    fix n
    show "p. p  carrier P  degree p  n  degree (p of g) = 0"
    proof(induction n)
      case 0
      then show ?case 
        by (simp add: assms(1) sub_const)       
    next
      case (Suc n)
      fix n
      assume IH: "p. p  carrier P  degree p  n  degree (p of g) = 0"
      show  "p. p  carrier P  degree p  (Suc n)  degree (p of g) = 0"
      proof-
        fix p
        assume A0: "p  carrier P"
        assume A1: "degree p  (Suc n)"
        show "degree (p of g) = 0"
        proof(cases "degree p < Suc n")
          case True
          then show ?thesis using IH 
            using A0 by auto
        next
          case False
          then have D: "degree p = Suc n" 
            by (simp add: A1 nat_less_le)
          show ?thesis
          proof-
            have P0: "degree ((trunc p) of g) = 0" using IH 
              by (metis A0 D less_Suc_eq_le trunc_degree trunc_closed zero_less_Suc)
            have P1: "degree ((ltrm p) of g) = 0" 
            proof-
              obtain a n where an_def: "ltrm p = monom P a n  a  carrier R"
                unfolding leading_term_def 
                using A0 P_def cfs_closed by blast
              obtain b where b_def: "g = monom P b 0  b  carrier R"
                using assms deg_zero_impl_monom  coeff_closed 
                by blast               
              have 0: " monom P b 0 [^]P n = monom P (b[^]n) 0"
                apply(induction n)
                 apply fastforce[1]
              proof- fix n::nat assume IH: "monom P b 0 [^]P n = monom P (b [^] n) 0"
                have "monom P b 0 [^]P Suc n = (monom P (b[^]n) 0) P monom P b 0"
                  using IH by simp
                then have "monom P b 0 [^]P Suc n = (monom P ((b[^]n)b) 0)"
                  using b_def  
                  by (simp add: monom_mult_is_smult monom_mult_smult)
                then show "monom P b 0 [^]P Suc n = monom P (b [^] Suc n) 0 "
                  by simp 
              qed
                   
              then have 0: "a P monom P b 0 [^]P n = monom P (a  b[^]n) 0"
                by (simp add: an_def b_def monom_mult_smult)


              then show ?thesis using monom_sub[of a "monom P b 0" n] assms an_def 
                by (simp add: a  carrier R; monom P b 0  carrier P  monom P a n of monom P b 0 = a P monom P b 0 [^]P n b_def)
            qed
            have P2: "p of g = (trunc p of g) P ((ltrm p) of g)"
              by (metis A0 assms(1) ltrm_closed sub_add trunc_simps(1) trunc_closed)
            then show ?thesis 
              using P0 P1 P2 deg_add[of "trunc p of g" "ltrm p of g"] 
              by (metis A0 assms(1) le_0_eq ltrm_closed max_0R sub_closed trunc_closed)              
          qed
        qed
      qed
    qed
  qed
  then show ?thesis 
    using assms(2) by blast
qed

lemma (in UP_cring)  cring_sub_deg_bound:
  assumes "g  carrier P"
  assumes "f  carrier P"
  shows "degree (f of g)  degree f * degree g"
proof-
  have "n.  p. p  carrier P  (degree p)  n  degree (p of g)  degree p * degree g"
  proof-
    fix n::nat
    show " p. p  carrier P  (degree p)  n  degree (p of g)  degree p * degree g"
    proof(induction n)
      case 0
      then have B0: "degree p = 0" by auto 
      then show ?case using sub_const[of g p] 
        by (simp add: "0.prems"(1) assms(1))
    next
      case (Suc n)
      fix n
      assume IH: "(p. p  carrier P  degree p  n  degree (p of g)  degree p * degree g)"
      show " p  carrier P  degree p  Suc n  degree (p of g)   degree p * degree g"
      proof-
        assume A0: "p  carrier P"
        assume A1: "degree p  Suc n"
        show ?thesis 
        proof(cases "degree p < Suc n")
          case True
          then show ?thesis using IH 
            by (simp add: A0)
        next
          case False
          then have D: "degree p = Suc n" 
            using A1 by auto  
          have P0: "(p of g) = ((trunc p) of g) P ((ltrm p) of g)"
            by (metis A0 assms(1) ltrm_closed sub_add trunc_simps(1) trunc_closed)
          have P1: "degree ((trunc p) of g)  (degree (trunc p))*(degree g)"
            using IH  by (metis A0 D less_Suc_eq_le trunc_degree trunc_closed zero_less_Suc)
          have P2: "degree ((ltrm p) of g)  (degree p) * degree g"
            using A0 D P_def  UP_cring_axioms assms(1) 
            by (metis False cfs_closed coeff_simp cring_sub_monom_bound deg_zero lcoeff_nonzero2 less_Suc_eq_0_disj)                              
          then show ?thesis
            proof(cases "degree g = 0")
              case True
              then show ?thesis 
                by (simp add: Suc(2) assms(1) sub_in_const)
            next
              case F: False
              then show ?thesis 
              proof-
                have P3: "degree ((trunc p) of g)  n*degree g"
                  using A0 False D  P1 P2  IH[of "trunc p"] trunc_degree[of p]
                proof -
                  { assume "degree (trunc p) < degree p"
                    then have "degree (trunc p)  n"
                      using D by auto
                    then have ?thesis
                      by (meson P1 le_trans mult_le_cancel2) }
                  then show ?thesis
                    by (metis (full_types) A0 D Suc_mult_le_cancel1 nat_mult_le_cancel_disj trunc_degree)
                qed      
                then have P3': "degree ((trunc p) of g) < (degree p)*degree g"
                  using F D by auto 
                have P4: "degree (ltrm p of g)  (degree p)*degree g"
                  using cring_sub_monom_bound  D P2 
                  by auto
                then show ?thesis 
                  using D  P0 P1 P3 P4 A0 P3' assms(1) bound_deg_sum less_imp_le_nat
                    ltrm_closed sub_closed trunc_closed 
                  by metis                   
              qed
            qed
          qed
        qed
      qed
  qed
  then show ?thesis 
    using assms(2) by blast
qed

lemma (in UP_cring)  cring_sub_deg:
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "lcf f  (lcf g [^] (degree f))  𝟬"
  shows "degree (f of g) = degree f * degree g"
proof-
  have 0: "f of g = (trunc f of g) P ((ltrm f) of g)"
    by (metis assms(1) assms(2) ltrm_closed rev_sub_add sub_rev_sub trunc_simps(1) trunc_closed)
  have 1: "lcf f  𝟬"
    using assms  cring.cring_simprules(26) lcf_closed 
    by auto
  have 2: "degree ((ltrm f) of g) = degree f * degree g"
    using 0 1 assms cring_sub_monom[of "lcf f" "ltrm f" "degree f" g] lcf_closed lcf_ltrm 
    by blast
  show ?thesis
    apply(cases "degree f = 0")
     apply (simp add: assms(1) assms(2))
    apply(cases "degree g = 0")
     apply (simp add: assms(1) assms(2) sub_in_const)
    using 0 1 assms cring_sub_deg_bound[of g "trunc f"] trunc_degree[of f]   
    using sub_const apply auto[1]
    apply(cases "degree g = 0")
    using 0 1 assms cring_sub_deg_bound[of g "trunc f"] trunc_degree[of f]   
    using sub_in_const apply fastforce
    unfolding 0 using 1 2 
    by (smt "0" ltrm_closed f  carrier P; 0 < deg R f  deg R (Cring_Poly.truncate R f) < deg R f
        assms(1) assms(2) cring_sub_deg_bound degree_of_sum_diff_degree equal_deg_sum
        le_eq_less_or_eq mult_less_cancel2 nat_neq_iff neq0_conv sub_closed trunc_closed)    
qed

lemma (in UP_domain)  sub_deg0:
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "g  𝟬P"
  assumes "f  𝟬P"
  shows "degree (f of g) = degree f * degree g"
proof-
  have "n.  p. p  carrier P  (degree p)  n  degree (p of g) = degree p * degree g"
  proof-
    fix n::nat
    show " p. p  carrier P  (degree p)  n  degree (p of g) = degree p * degree g"
    proof(induction n)
      case 0
      then have B0: "degree p = 0" by auto 
      then show ?case using sub_const[of g p] 
        by (simp add: "0.prems"(1) assms(1))
    next
      case (Suc n)
      fix n
      assume IH: "(p. p  carrier P  degree p  n  degree (p of g) = degree p * degree g)"
      show " p  carrier P  degree p  Suc n  degree (p of g) = degree p * degree g"
      proof-
        assume A0: "p  carrier P"
        assume A1: "degree p  Suc n"
        show ?thesis 
        proof(cases "degree p < Suc n")
          case True
          then show ?thesis using IH 
            by (simp add: A0)
        next
          case False
          then have D: "degree p = Suc n" 
            using A1 by auto  
          have P0: "(p of g) = ((trunc p) of g) P ((ltrm p) of g)"
            by (metis A0 assms(1) ltrm_closed sub_add trunc_simps(1) trunc_closed)
          have P1: "degree ((trunc p) of g) = (degree (trunc p))*(degree g)"
            using IH  by (metis A0 D less_Suc_eq_le trunc_degree trunc_closed zero_less_Suc)
          have P2: "degree ((ltrm p) of g) = (degree p) * degree g"
            using A0 D P_def UP_domain.sub_monom UP_cring_axioms assms(1) 
            by (metis False UP_domain_axioms UP_ring.coeff_simp UP_ring.lcoeff_nonzero2 UP_ring_axioms cfs_closed deg_nzero_nzero less_Suc_eq_0_disj)
                     
          then show ?thesis
            proof(cases "degree g = 0")
              case True
              then show ?thesis 
                by (simp add: Suc(2) assms(1) sub_in_const)
            next
              case False
              then show ?thesis 
              proof-
                have P3: "degree ((trunc p) of g) < degree ((ltrm p) of g)"
                  using False D  P1 P2  
                  by (metis (no_types, lifting) A0 mult.commute mult_right_cancel 
                      nat_less_le nat_mult_le_cancel_disj trunc_degree zero_less_Suc)
                then show ?thesis 
                 by (simp add: A0 ltrm_closed P0 P2 assms(1) equal_deg_sum sub_closed trunc_closed)                 
              qed
            qed
          qed
        qed
      qed
  qed
  then show ?thesis 
      using assms(2) by blast
qed

lemma(in UP_domain)  sub_deg:
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "g  𝟬P"
  shows "degree (f of g) = degree f * degree g"
proof(cases "f = 𝟬P")
  case True
  then show ?thesis 
    using assms(1)  sub_const by auto
next
  case False
  then show ?thesis 
    by (simp add: assms(1) assms(2) assms(3) sub_deg0)
qed

lemma(in UP_cring)  cring_ltrm_sub:
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "degree g > 0"
  assumes "lcf f  (lcf g [^] (degree f))  𝟬"
  shows "ltrm (f of g) = ltrm ((ltrm f) of g)"
proof-
  have P0: "degree (f of g) = degree ((ltrm f) of g)"
    using assms(1) assms(2) assms(4) cring_sub_deg lcf_eq ltrm_closed deg_ltrm 
    by auto               
  have P1: "f of g = ((trunc f) of g) P((ltrm f) of g)"
    by (metis assms(1) assms(2) ltrm_closed rev_sub_add sub_rev_sub trunc_simps(1) trunc_closed)
  then show ?thesis
  proof(cases "degree f = 0")
    case True
    then show ?thesis 
      using ltrm_deg_0 assms(2) by auto     
  next
    case False
    have P2: "degree (f of g) = degree f * degree g"
      by (simp add: assms(1) assms(2) assms(4) cring_sub_deg)      
    then have P3: "degree ((trunc f) of g) < degree ((ltrm f) of g)"
      using False P0 P1 P_def UP_cring.sub_closed trunc_closed UP_cring_axioms
          UP_ring.degree_of_sum_diff_degree UP_ring.ltrm_closed UP_ring_axioms assms(1) 
          assms(2) assms(4) cring_sub_deg_bound le_antisym less_imp_le_nat less_nat_zero_code
          mult_right_le_imp_le nat_neq_iff trunc_degree
      by (smt assms(3))
    then show ?thesis using P0 P1 P2 
      by (metis (no_types, lifting) ltrm_closed ltrm_of_sum_diff_degree P.add.m_comm assms(1) assms(2) sub_closed trunc_closed)      
  qed
qed

lemma(in UP_domain)  ltrm_sub:
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "degree g > 0"
  shows "ltrm (f of g) = ltrm ((ltrm f) of g)"
proof-
  have P0: "degree (f of g) = degree ((ltrm f) of g)"
    using sub_deg 
    by (metis ltrm_closed assms(1) assms(2) assms(3) deg_zero deg_ltrm nat_neq_iff)       
  have P1: "f of g = ((trunc f) of g) P((ltrm f) of g)"
    by (metis assms(1) assms(2) ltrm_closed rev_sub_add sub_rev_sub trunc_simps(1) trunc_closed)
  then show ?thesis
  proof(cases "degree f = 0")
    case True
    then show ?thesis 
      using ltrm_deg_0 assms(2) by auto    
  next
    case False
    then have P2: "degree ((trunc f) of g) < degree ((ltrm f) of g)"
      using sub_deg 
      by (metis (no_types, lifting) ltrm_closed assms(1) assms(2) assms(3) deg_zero
          deg_ltrm mult_less_cancel2 neq0_conv trunc_closed trunc_degree)      
    then show ?thesis 
      using P0 P1 P2 
      by (metis (no_types, lifting) ltrm_closed ltrm_of_sum_diff_degree P.add.m_comm assms(1) assms(2) sub_closed trunc_closed)
  qed
qed

lemma(in UP_cring)  cring_lcf_of_sub_in_ltrm:
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "degree f = n"
  assumes "degree g > 0"
  assumes "(lcf f)  ((lcf g)[^]n) 𝟬"
  shows "lcf ((ltrm f) of g) = (lcf f)  ((lcf g)[^]n)"
  by (metis (no_types, lifting) P.nat_pow_closed P_def R.r_null UP_cring.monom_sub UP_cring_axioms 
      assms(1) assms(2) assms(3) assms(5) cfs_closed cring_lcf_pow cring_lcf_scalar_mult)

lemma(in UP_domain)  lcf_of_sub_in_ltrm:
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "degree f = n"
  assumes "degree g > 0"
  shows "lcf ((ltrm f) of g) = (lcf f)  ((lcf g)[^]n)"
proof(cases "degree f = 0")
  case True
  then show ?thesis 
    using ltrm_deg_0 assms(1) assms(2) assms(3) cfs_closed 
    by (simp add: sub_const)       
next
  case False
  then show ?thesis 
  proof-
    have P0: "(ltrm f) of g = (to_poly (lcf f)) P (g[^]Pn)"
      unfolding compose_def
      using assms UP_pre_univ_prop.eval_monom[of R P to_poly "(lcf f)" g n] to_poly_UP_pre_univ_prop 
      unfolding P_def  
      using P_def cfs_closed by blast        
    have P1: "(ltrm f) of g = (lcf f) P(g[^]Pn)"
      using P0 P.nat_pow_closed 
      by (simp add: assms(1) assms(2) assms(3) cfs_closed monom_sub)          
    have P2: "ltrm ((ltrm f) of g) = (ltrm (to_poly (lcf f))) P (ltrm (g[^]Pn))"
      using P0 ltrm_mult P.nat_pow_closed P_def assms(1) assms(2) 
         to_poly_closed 
      by (simp add: cfs_closed)                  
    have P3: "ltrm ((ltrm f) of g) =  (to_poly (lcf f)) P (ltrm (g[^]Pn))"
      using P2  ltrm_deg_0 assms(2) to_poly_closed 
      by (simp add: cfs_closed)           
    have P4: "ltrm ((ltrm f) of g) = (lcf f) P ((ltrm g)[^]Pn)"
      using P.nat_pow_closed P1 P_def assms(1) assms(2) ltrm_pow0 ltrm_smult 
      by (simp add: cfs_closed)
    have P5: "lcf ((ltrm f) of g) = (lcf f)  (lcf ((ltrm g)[^]Pn))"
      using lcf_scalar_mult P4  by (metis P.nat_pow_closed P1 cfs_closed 
          UP_smult_closed assms(1) assms(2) assms(3) lcf_eq ltrm_closed sub_rev_sub)
    show ?thesis
      using P5 ltrm_pow lcf_pow assms(1) lcf_eq ltrm_closed by presburger
  qed
qed

lemma(in UP_cring)  cring_ltrm_of_sub_in_ltrm:
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "degree f = n"
  assumes "degree g > 0"
  assumes "(lcf f)  ((lcf g)[^]n) 𝟬"
  shows "ltrm ((ltrm f) of g) = (lcf f) P ((ltrm g)[^]Pn)"
  by (smt lcf_eq ltrm_closed R.nat_pow_closed R.r_null assms(1) assms(2) assms(3) 
      assms(4) assms(5) cfs_closed cring_lcf_of_sub_in_ltrm cring_lcf_pow cring_pow_ltrm
      cring_pow_deg cring_sub_deg deg_zero deg_ltrm monom_mult_smult neq0_conv)
  
lemma(in UP_domain)  ltrm_of_sub_in_ltrm:
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "degree f = n"
  assumes "degree g > 0"
  shows "ltrm ((ltrm f) of g) = (lcf f) P ((ltrm g)[^]Pn)"
  by (smt Group.nat_pow_0 lcf_of_sub_in_ltrm lcf_pow lcf_scalar_mult ltrm_closed 
      ltrm_pow0 ltrm_smult P.nat_pow_closed P_def UP_ring.monom_one UP_ring_axioms assms(1) 
      assms(2) assms(3) assms(4) cfs_closed coeff_simp deg_const deg_nzero_nzero deg_pow 
      deg_smult deg_ltrm lcoeff_nonzero2 nat_less_le sub_deg)

text‹formula for the leading term of a composition ›

lemma(in UP_domain)  cring_ltrm_of_sub:
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "degree f = n"
  assumes "degree g > 0"
  assumes "(lcf f)  ((lcf g)[^]n) 𝟬"
  shows "ltrm (f of g) = (lcf f) P ((ltrm g)[^]Pn)"
  using ltrm_of_sub_in_ltrm ltrm_sub assms(1) assms(2) assms(3) assms(4) by presburger
    
lemma(in UP_domain)  ltrm_of_sub:
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "degree f = n"
  assumes "degree g > 0"
  shows "ltrm (f of g) = (lcf f) P ((ltrm g)[^]Pn)"
  using ltrm_of_sub_in_ltrm ltrm_sub assms(1) assms(2) assms(3) assms(4) by presburger
   
text‹subtitution is associative›

lemma  sub_assoc_monom:
  assumes "f  carrier P"
  assumes "q  carrier P"
  assumes "r  carrier P"
  shows "(ltrm f) of (q of r) = ((ltrm f) of q) of r"
proof-
  obtain n where n_def: "n = degree f"
    by simp
  obtain a where a_def: "a  carrier R  (ltrm f) = monom P a n"
    using assms(1) cfs_closed n_def by blast         
  have LHS: "(ltrm f) of (q of r) = a P (q of r)[^]P n"
    by (metis P.nat_pow_closed P_def UP_pre_univ_prop.eval_monom a_def assms(2)
        assms(3) compose_def monom_mult_is_smult sub_closed to_poly_UP_pre_univ_prop to_polynomial_def)
  have RHS0: "((ltrm f) of q) of r = (a P q[^]P n)of r"
    by (metis P.nat_pow_closed P_def UP_pre_univ_prop.eval_monom a_def 
        assms(2) compose_def monom_mult_is_smult to_poly_UP_pre_univ_prop to_polynomial_def)
  have RHS1: "((ltrm f) of q) of r = ((to_poly a) P q[^]P n)of r"
    using RHS0  by (metis P.nat_pow_closed P_def a_def 
        assms(2) monom_mult_is_smult to_polynomial_def)
  have RHS2: "((ltrm f) of q) of r = ((to_poly a) of r) P (q[^]P n of r)"
    using RHS1 a_def assms(2) assms(3) sub_mult to_poly_closed by auto
  have RHS3: "((ltrm f) of q) of r = (to_poly a) P (q[^]P n of r)"
    using RHS2 a_def assms(3) sub_to_poly by auto
  have RHS4: "((ltrm f) of q) of r = a P ((q[^]P n)of r)"
    using RHS3 
    by (metis P.nat_pow_closed P_def a_def assms(2) assms(3) 
        monom_mult_is_smult sub_closed to_polynomial_def)
  have "(q of r)[^]P n = ((q[^]P n)of r)" 
    apply(induction n) 
    apply (metis Group.nat_pow_0 P.ring_simprules(6) assms(3) deg_one sub_const)
    by (simp add: assms(2) assms(3) sub_mult)    
  then show ?thesis using RHS4 LHS by simp
qed

lemma sub_assoc:
  assumes "f  carrier P"
  assumes "q  carrier P"
  assumes "r  carrier P"
  shows "f of (q of r) = (f of q) of r"
proof-
  have " n.  p. p  carrier P  degree p  n  p of (q of r) = (p of q) of r"
  proof-
    fix n
    show " p. p  carrier P  degree p  n  p of (q of r) = (p of q) of r"
    proof(induction n)
      case 0
      then have deg_p: "degree p = 0"
        by blast
      then have B0: "p of (q of r) = p"
        using sub_const[of "q of r" p] assms  "0.prems"(1) sub_closed by blast
      have B1: "(p of q) of r = p"
      proof-
        have p0: "p of q = p"
          using deg_p 0 assms(2) 
          by (simp add: P_def UP_cring.sub_const UP_cring_axioms)                             
        show ?thesis 
          unfolding p0 using deg_p 0 assms(3) 
          by (simp add: P_def UP_cring.sub_const UP_cring_axioms)                             
      qed
      then show "p of (q of r) = (p of q) of r" using B0 B1 by auto 
    next
      case (Suc n)
      fix n
      assume IH: " p. p  carrier P  degree p  n  p of (q of r) = (p of q) of r"
      then show " p. p  carrier P  degree p  Suc n  p of (q of r) = (p of q) of r"
      proof-
        fix p
        assume A0: " p  carrier P "
        assume A1: "degree p  Suc n"
        show "p of (q of r) = (p of q) of r"
        proof(cases "degree p < Suc n")
          case True
          then show ?thesis using A0 A1 IH by auto 
        next
          case False
          then have "degree p = Suc n"
            using A1 by auto 
          have I0: "p of (q of r) = ((trunc p) P (ltrm p)) of (q of r)"
            using A0 trunc_simps(1) by auto
          have I1: "p of (q of r) = ((trunc p)  of (q of r)) P ((ltrm p)  of (q of r))"
            using I0 sub_add 
            by (simp add: A0 assms(2) assms(3) ltrm_closed rev_sub_closed sub_rev_sub trunc_closed)
          have I2: "p of (q of r) = (((trunc p)  of q) of r) P (((ltrm p)  of q) of r)"
            using IH[of "trunc p"] sub_assoc_monom[of p q r] 
            by (metis A0 I1 ‹degree p = Suc n assms(2) assms(3) 
                less_Suc_eq_le trunc_degree trunc_closed zero_less_Suc)
          have I3: "p of (q of r) = (((trunc p)  of q) P ((ltrm p)  of q)) of r"
            using sub_add trunc_simps(1) assms   
            by (simp add: A0 I2 ltrm_closed sub_closed trunc_closed)
          have I4: "p of (q of r) = (((trunc p)P(ltrm p))   of q)  of r"
            using sub_add trunc_simps(1) assms   
            by (simp add: trunc_simps(1) A0 I3 ltrm_closed trunc_closed)
          then show ?thesis 
            using A0 trunc_simps(1) by auto
        qed
      qed
    qed
  qed
  then show ?thesis 
    using assms(1) by blast
qed

lemma sub_smult:
  assumes "f  carrier P"
  assumes "q  carrier P"
  assumes "a  carrier R"
  shows "(aPf ) of q = aP(f of q)"
proof-
  have "(aPf ) of q = ((to_poly a) Pf) of q"
    using assms  by (metis P_def monom_mult_is_smult to_polynomial_def)
  then have "(aPf ) of q = ((to_poly a) of q) P(f of q)"
    by (simp add: assms(1) assms(2) assms(3) sub_mult to_poly_closed)
  then have "(aPf ) of q = (to_poly a) P(f of q)"
    by (simp add: assms(2) assms(3) sub_to_poly)
  then show ?thesis 
    by (metis P_def assms(1) assms(2) assms(3) 
        monom_mult_is_smult sub_closed to_polynomial_def)
qed

lemma to_fun_sub_monom:
  assumes "is_UP_monom f"
  assumes "g  carrier P"
  assumes "a  carrier R"
  shows "to_fun (f of g) a = to_fun f (to_fun g a)"   
proof-
  obtain b n where b_def: "b  carrier R  f = monom P b n"
    using assms unfolding is_UP_monom_def 
     using P_def cfs_closed by blast     
  then have P0: "f of g = b P (g[^]Pn)"
    using b_def assms(2) monom_sub by blast
  have P1: "UP_pre_univ_prop R R (λx. x)" 
    by (simp add: UP_pre_univ_prop_fact)
  then have P2: "to_fun f (to_fun g a) = b ((to_fun g a)[^]n)"
    using P1 to_fun_eval[of f "to_fun g a"] P_def UP_pre_univ_prop.eval_monom assms(1)
          assms(2) assms(3) b_def is_UP_monomE(1) to_fun_closed 
    by force
  have P3: "to_fun (monom P b n of g) a =  b ((to_fun g a)[^]n)"
  proof-
    have 0: "to_fun (monom P b n of g) a = eval R R (λx. x) a  (b P (g[^]Pn) )"

      using UP_pre_univ_prop.eval_monom[of R "(UP R)" to_poly b g n]
            P_def assms(2) b_def to_poly_UP_pre_univ_prop to_fun_eval P0
      by (metis assms(3) monom_closed sub_closed)      
    have 1: "to_fun (monom P b n of g) a = (eval R R (λx. x) a  (to_poly b))  ( eval R R (λx. x) a ( g [^]UP R n ))"
      using 0 eval_ring_hom 
      by (metis P.nat_pow_closed P0 P_def assms(2) assms(3) b_def monom_mult_is_smult to_fun_eval to_fun_mult to_poly_closed to_polynomial_def)                
    have 2: "to_fun (monom P b n of g) a = b  ( eval R R (λx. x) a ( g [^]UP R n ))"
      using 1 assms(3) b_def to_fun_eval to_fun_to_poly to_poly_closed by auto              
    then show ?thesis 
      unfolding to_function_def to_fun_def 
      using eval_ring_hom P_def UP_pre_univ_prop.ring_homD UP_pre_univ_prop_fact 
            assms(2) assms(3) ring_hom_cring.hom_pow by fastforce
  qed
  then show ?thesis 
    using b_def P2 by auto
qed

lemma to_fun_sub:
  assumes "g  carrier P"
  assumes "f  carrier P"
  assumes "a  carrier R"
  shows "to_fun (f of  g) a = (to_fun f) (to_fun g a)"
proof(rule poly_induct2[of f])
  show "f  carrier P" 
    using assms by auto 
  show "p. p  carrier P  degree p = 0  to_fun (p of g) a = to_fun p (to_fun g a)"
  proof-
    fix p
    assume A0: "p  carrier P"
    assume A1: "degree p = 0"
    then have P0: "degree (p of g) = 0"
      by (simp add: A0 assms(1) sub_const)    
    then obtain b where b_def: "p of g = to_poly b  b  carrier R"
      using A0 A1 cfs_closed assms(1) to_poly_inverse 
      by (meson sub_closed)
    then have "to_fun (p of g) a = b" 
      by (simp add: assms(3) to_fun_to_poly)     
    have "p of g = p"
      using A0 A1 P_def sub_const UP_cring_axioms assms(1) by blast
    then have P1: "p = to_poly b"
      using b_def by auto
    have "to_fun g a  carrier R"
      using assms
      by (simp add: to_fun_closed)  
    then show "to_fun (p of g) a =  to_fun p (to_fun g a)"
      using P1 ‹to_fun (p of g) a = b b_def 
      by (simp add: to_fun_to_poly)      
  qed
  show "p. 0 < degree p  p  carrier P  
            to_fun (trunc p of g) a = to_fun (trunc p) (to_fun g a)  
            to_fun (p of g) a = to_fun p (to_fun g a)"
  proof-
    fix p
    assume A0: "0 < degree p"
    assume A1: " p  carrier P"
    assume A2: "to_fun (trunc p of g) a = to_fun (trunc p) (to_fun g a)"
    show "to_fun (p of g) a = to_fun p (to_fun g a)"
    proof-
      have "p of g = (trunc p) of g P (ltrm p) of g"
        by (metis A1 assms(1) ltrm_closed sub_add trunc_simps(1) trunc_closed)
      then have "to_fun (p of g) a = to_fun ((trunc p) of g) a  (to_fun ((ltrm p) of g) a)"
        by (simp add: A1 assms(1) assms(3) to_fun_plus ltrm_closed sub_closed trunc_closed)        
      then have 0: "to_fun (p of g) a = to_fun (trunc p) (to_fun g a)  (to_fun ((ltrm p) of g) a)"
        by (simp add: A2)
      have "(to_fun ((ltrm p) of g) a) = to_fun (ltrm p) (to_fun g a)"
        using to_fun_sub_monom 
        by (simp add: A1 assms(1) assms(3) ltrm_is_UP_monom)
      then have "to_fun (p of g) a = to_fun (trunc p) (to_fun g a)   to_fun (ltrm p) (to_fun g a)"
        using 0 by auto 
      then show ?thesis 
        by (metis A1 assms(1) assms(3) to_fun_closed to_fun_plus ltrm_closed trunc_simps(1) trunc_closed)
    qed
  qed
qed
end


text‹More material on constant terms and constant coefficients›

context UP_cring
begin

lemma to_fun_ctrm:
  assumes "f  carrier P"
  assumes  "b  carrier R"  
  shows "to_fun (ctrm f) b = (f 0)"
  using assms 
  by (metis ctrm_degree ctrm_is_poly lcf_monom(2) P_def cfs_closed to_fun_to_poly to_poly_inverse)   

lemma to_fun_smult:
  assumes "f  carrier P"
  assumes "b  carrier R"
  assumes "c  carrier R"
  shows "to_fun (c P f) b = c (to_fun f b)"
proof-
  have "(c P f) = (to_poly c) P f"
    by (metis P_def assms(1) assms(3) monom_mult_is_smult to_polynomial_def)
  then have "to_fun (c P f) b = to_fun (to_poly c) b  to_fun f b"
    by (simp add: assms(1) assms(2) assms(3) to_fun_mult to_poly_closed)    
  then show  ?thesis 
    by (simp add: assms(2) assms(3) to_fun_to_poly)  
qed

lemma to_fun_monom:
  assumes "c  carrier R"
  assumes "x  carrier R"
  shows "to_fun (monom P c n) x = c  x [^] n"
  by (smt P_def R.m_comm R.nat_pow_closed UP_cring.to_poly_nat_pow UP_cring_axioms assms(1) 
      assms(2) monom_is_UP_monom(1) sub_monom(1) to_fun_smult to_fun_sub_monom to_fun_to_poly 
      to_poly_closed to_poly_mult_simp(2))

lemma zcf_monom:
  assumes "a  carrier R"
  shows "zcf (monom P a n) = to_fun (monom P a n) 𝟬"
  using to_fun_monom unfolding zcf_def 
  by (simp add: R.nat_pow_zero assms cfs_monom)

lemma zcf_to_fun: 
  assumes "p  carrier P"
  shows "zcf p = to_fun p 𝟬"
  apply(rule poly_induct3[of p])
  apply (simp add: assms)
  using R.zero_closed zcf_add to_fun_plus apply presburger
  using zcf_monom by blast

lemma zcf_to_poly[simp]:
  assumes "a  carrier R"
  shows "zcf (to_poly a) = a"
  by (metis assms cfs_closed degree_to_poly to_fun_to_poly to_poly_inverse to_poly_closed zcf_def)

lemma zcf_ltrm_mult:
  assumes "p  carrier P"
  assumes "q  carrier P"
  assumes "degree p > 0"
  shows "zcf((ltrm p) P q) = 𝟬"
  using zcf_to_fun[of "ltrm p P q" ]
  by (metis ltrm_closed P.l_null P.m_closed R.zero_closed UP_zero_closed zcf_to_fun
      zcf_zero assms(1) assms(2) assms(3) coeff_ltrm to_fun_mult)

lemma zcf_mult:
  assumes "p  carrier P"
  assumes "q  carrier P"
  shows "zcf(p P q) = (zcf p)  (zcf q)"
  using zcf_to_fun[of " p P