Theory example_Bicomplex

Up to index of Isabelle/HOL/HOL-Algebra/example_Bicomplex

theory example_Bicomplex
imports MatrixLP BPL_classes_2008 Eval Efficient_Nat
begin

(*  Title:      code_generation/example_Bicomplex.thy
    ID:         $Id: example_Bicomplex.thy $
    Author:     Jesus Aransay
*)

theory example_Bicomplex
  imports
  "~~/src/HOL/Real/Float"
  "Matrix/SparseMatrix" (*There is a problem in the 
  Matrix.thy file in Isabelle 2008 with the "(overloaded)" definition of @{text "zero_matrix_def"}
  that we avoid using our own files. New releases of Isabelle fix this problem.*)
  "Matrix/cplex/MatrixLP"
  "BPL_classes_2008"
  "~~/src/HOL/Library/Eval" (*This library provides better support of the @{term "value"} tool*)
  "~~/src/HOL/Library/Efficient_Nat" (*This must be always the last library to be loaded*)
begin

section{*An example of the BPL based on bicomplexes.*}

text{*We fit a concrete example of the BPL into the code previously generated*}
text{*The example is as follows: 
  we have a "big" differential group $D$ which will be represented 
  as matrices of any dimension of integers, with usual addition of matrices 
  (two matrices of different dimension can be added obtaining as result
  a matrix with the greatest number of rows and the greatest number of columns 
  between the two matrices being added), and where the null matrix is any 
  matrix containing exclusively zeros.*} 
text{*The minus operation is the result of applying minus to every component 
  of a matrix. Such structure can be proved to be an abelian group.*}
text{*Then, the differential $d_{D}$ is defined 
  over the matrices by considering various particular cases 
  (by means of an @{text "if"} structure). The homology operator $h$ and 
  the perturbation $\delta_{D}$ are also defined over matrices.*}
text{*The nilpotency condition is locally satisfied for the number of columns 
  of a matrix plus one.*}
text{*The small differential group $C$ is null.*}
text{*Then, $f$ and $g$ are also null.*}

lemma "- (- x) = (x::'a::lordered_ring)" 
  using minus_minus [of x] by simp

lemma assumes x_n_0: "x ≠ 0" 
  shows "(- x) ≠ (0::'a::lordered_ring)" 
  using x_n_0 by simp

lemma assumes x_n_0: "(- x) ≠ 0" 
  shows "x ≠ (0::'a::lordered_ring)" 
  using x_n_0 by simp

lemma nonzero_positions_minus_f:
  fixes f:: "'a::lordered_ring matrix => 'b::lordered_ring matrix"
  shows "nonzero_positions (Rep_matrix (f A)) 
  = nonzero_positions (Rep_matrix ((- f) A))"
  unfolding nonzero_positions_def 
  unfolding fun_Compl_def 
  using Rep_minus [of "f A"] by simp

subsection{*Definition of the differential.*}

definition  diff_infmatrix :: "'a::{zero} infmatrix => 'a infmatrix"
  where diff_infmatrix_def :
  "diff_infmatrix A = (%i::nat. %j::nat. if ((i + j) mod 2 = 1) then 0
                                                       else  (A i (j + 1)))"

definition diff_inv :: "(nat × nat) => (nat × nat)"
  where diff_inv_def : "diff_inv pos = ((fst pos, (snd pos) + 1))"

lemma diff_infmatrix_finite[simp]: 
  shows "finite (nonzero_positions (diff_infmatrix (Rep_matrix x)))"
proof (rule finite_imageD [of diff_inv])
  let ?B = "nonzero_positions (diff_infmatrix (Rep_matrix x))"
  show "finite (diff_inv ` ?B)"
  proof (rule finite_subset [of _ "{i. Rep_matrix x (fst i) (snd i) ≠ 0}"])
    show "diff_inv` ?B ⊆ {i. Rep_matrix x (fst i) (snd i) ≠ 0}"
      unfolding image_def
      unfolding diff_inv_def
      unfolding diff_infmatrix_def
      unfolding if_def
      unfolding nonzero_positions_def by force
    show "finite {i::nat × nat. Rep_matrix x (fst i) (snd i) ≠ (0::'a)}"
      using finite_nonzero_positions [of x] 
      unfolding nonzero_positions_def [of "Rep_matrix x"] by simp
  qed
  show "inj_on diff_inv ?B"
    unfolding inj_on_def
    unfolding diff_inv_def by auto
qed

lemma diff_infmatrix_matrix: 
  shows "(diff_infmatrix (Rep_matrix (x::'a::zero matrix))) ∈ matrix"
  unfolding matrix_def using diff_infmatrix_finite by simp

lemma diff_infmatrix_closed[simp]:
  "Rep_matrix (Abs_matrix (diff_infmatrix (Rep_matrix x))) 
  = diff_infmatrix (Rep_matrix x)"
  using Abs_matrix_inverse [OF diff_infmatrix_matrix [of x]] .

lemma diff_infmatrix_is_matrix: assumes as: "A ∈ matrix" 
  shows "diff_infmatrix A ∈ matrix"
  unfolding matrix_def
  unfolding mem_Collect_eq
proof (rule finite_imageD [of diff_inv])
  let ?B = "nonzero_positions (diff_infmatrix A)" 
  show "finite (diff_inv ` ?B)"
  proof (rule finite_subset [of _ "{i. A (fst i) (snd i) ≠ 0}"])
    show "diff_inv` ?B ⊆ {i. A (fst i) (snd i) ≠ 0}"
      unfolding image_def
      unfolding diff_inv_def
      unfolding diff_infmatrix_def
      unfolding if_def
      unfolding nonzero_positions_def by force
    show "finite {i::nat × nat. A (fst i) (snd i) ≠ (0::'a)}"
      using as
      unfolding matrix_def
      unfolding nonzero_positions_def
      unfolding mem_Collect_eq .
  qed
  show "inj_on diff_inv ?B"
    unfolding inj_on_def
    unfolding diff_inv_def by auto
qed

lemma diff_infmatrix_twice [simp]:
  shows "diff_infmatrix (diff_infmatrix A) = (Rep_matrix 0)"
  unfolding diff_infmatrix_def 
  by (rule ext, rule ext, auto simp add: Rep_matrix_inject) arith

text{*The name @{term "diff_matrix"} is already used to 
  express the difference between two matrices*}

definition diff_matrix1 :: "'a::lordered_ring matrix => 'a matrix"
  where diff_matrix1_def: "diff_matrix1 = Abs_matrix o diff_infmatrix o Rep_matrix"

lemma combine_infmatrix_matrix:
  shows "f 0 0 = 0 ==> combine_infmatrix f (Rep_matrix A) (Rep_matrix B) ∈ matrix"
  unfolding matrix_def
  using finite_subset[of _ "(nonzero_positions (Rep_matrix A)) 
    ∪ (nonzero_positions (Rep_matrix B))"]
  by simp

lemma combine_infmatrix_diff_infmatrix_matrix[simp]: 
  "f 0 0 = 0 ==> combine_infmatrix f 
  (diff_infmatrix (Rep_matrix A)) (diff_infmatrix (Rep_matrix B)) ∈ matrix"
  unfolding matrix_def
  using finite_subset [of _ "(nonzero_positions (diff_infmatrix (Rep_matrix A))) ∪ 
    (nonzero_positions (diff_infmatrix (Rep_matrix B)))"] by simp

lemma diff_infmatrix_combine_infmatrix_matrix[simp]:
  "f 0 0 = 0 ==> diff_infmatrix (combine_infmatrix f (Rep_matrix A) 
  (Rep_matrix B)) ∈ matrix"
proof (unfold matrix_def, simp, rule finite_imageD [of diff_inv])
  let ?B = "nonzero_positions (diff_infmatrix (combine_infmatrix 
    f (Rep_matrix A) (Rep_matrix B)))"
  assume f_0: "f (0::'b) (0::'c) = (0::'a)"
  show "finite (diff_inv ` ?B)"
  proof -
    have "diff_inv` ?B ⊆ {i. f (Rep_matrix A (fst i) (snd i)) 
      (Rep_matrix B (fst i) (snd i)) ≠ 0}"
      unfolding diff_infmatrix_def 
      unfolding combine_infmatrix_def
      unfolding image_def
      unfolding nonzero_positions_def
      unfolding diff_inv_def
      unfolding if_def by force
    with combine_infmatrix_matrix [of f] f_0 show ?thesis
      unfolding matrix_def 
      unfolding combine_infmatrix_def 
      unfolding nonzero_positions_def by (simp add: finite_subset)
  qed
  show "inj_on diff_inv ?B" unfolding inj_on_def diff_inv_def by auto
qed

subsection{*Matrices as an instance of differential group.*}

lemma Abs_matrix_inject2: 
  assumes x: "x ∈ matrix" and y: "y ∈ matrix" and eq: "x = y"
  shows "Abs_matrix x = Abs_matrix y"
  using Abs_matrix_inject [OF x y] using eq by simp

lemma diff_matrix1_hom: 
  shows "diff_matrix1 (A + B) = diff_matrix1 A + diff_matrix1 B"
  unfolding plus_matrix_def combine_matrix_def diff_matrix_def diff_matrix1_def
proof (simp add: expand_fun_eq)
  show "Abs_matrix (diff_infmatrix (combine_infmatrix op + (Rep_matrix A) 
    (Rep_matrix B))) =
    Abs_matrix (combine_infmatrix op + 
    (diff_infmatrix (Rep_matrix A)) (diff_infmatrix (Rep_matrix B)))"
  proof (rule Abs_matrix_inject2)
    show "combine_infmatrix op + (diff_infmatrix (Rep_matrix A)) 
      (diff_infmatrix (Rep_matrix B)) ∈ matrix"
      using combine_infmatrix_diff_infmatrix_matrix [of "op + :: 'a => 'a => 'a"]
      by simp
    show diff_matrix: "diff_infmatrix (combine_infmatrix op + (Rep_matrix A) 
      (Rep_matrix B)) ∈ matrix" 
      using diff_infmatrix_combine_infmatrix_matrix [of "op + :: 'a => 'a => 'a"]
      by simp
    show "diff_infmatrix (combine_infmatrix op + (Rep_matrix A) (Rep_matrix B)) =
      combine_infmatrix op + (diff_infmatrix (Rep_matrix A)) 
      (diff_infmatrix (Rep_matrix B))"
      unfolding combine_infmatrix_def 
      unfolding diff_infmatrix_def by (rule ext)+ auto
  qed
qed

lemma diff_matrix1_twice: 
  shows "diff_matrix1 (diff_matrix1 A) = (0::('a::lordered_ring matrix))"
  unfolding diff_matrix1_def by simp

instantiation matrix :: (lordered_ring) diff_group_add
begin

definition diff_matrix_def: "diff A == diff_matrix1 A"

instance
proof
  fix A B :: "'a matrix"
  show "d (A + B) = d A + d B"
    using diff_matrix1_hom [of A B] unfolding diff_matrix_def .
  show "diff_group_add_class.diff o diff_group_add_class.diff 
    = (λx::'a::lordered_ring matrix. 0::'a matrix)"
    apply (rule ext)
    unfolding o_apply
    unfolding diff_matrix_def
    unfolding diff_matrix1_twice ..
qed

end

subsection{*Definition of the perturbation $\delta_{D}$.*}

definition pert_infmatrix :: "'a::{zero} infmatrix => 'a infmatrix"
  where pert_infmatrix_def : "pert_infmatrix A = 
            (%i::nat. %j::nat. if ((i + j) mod 2 = 1) then 0
                                                      else (A (i + 1) j))"

definition pert_inv :: "(nat × nat) => (nat × nat)"
  where pert_inv_def : "pert_inv pos = ((fst pos) + 1, snd pos)"

lemma pert_infmatrix_finite[simp]: 
  shows "finite (nonzero_positions (pert_infmatrix (Rep_matrix x)))"
proof (rule finite_imageD [of pert_inv])
  let ?B = "nonzero_positions (pert_infmatrix (Rep_matrix x))"
  show "finite (pert_inv ` ?B)"
  proof (rule finite_subset [of _ "{i. Rep_matrix x (fst i) (snd i) ≠ 0}"])
    show "pert_inv` ?B ⊆ {i. Rep_matrix x (fst i) (snd i) ≠ 0}"
      unfolding pert_infmatrix_def 
      unfolding image_def 
      unfolding nonzero_positions_def 
      unfolding pert_inv_def 
      unfolding if_def by force
    show "finite {i::nat × nat. Rep_matrix x (fst i) (snd i) ≠ (0::'a)}"
      using finite_nonzero_positions [of x]
      unfolding nonzero_positions_def [of "Rep_matrix x"] .
  qed
  show "inj_on pert_inv ?B" unfolding inj_on_def pert_inv_def by auto
qed

lemma pert_infmatrix_matrix:
  shows "(pert_infmatrix (Rep_matrix (x::'a::zero matrix))) ∈ matrix"
  unfolding matrix_def by (simp add: pert_infmatrix_finite)

lemma pert_infmatrix_closed[simp]:
  "Rep_matrix (Abs_matrix (pert_infmatrix (Rep_matrix x))) 
  = pert_infmatrix (Rep_matrix x)"
  by (rule Abs_matrix_inverse) (simp only: pert_infmatrix_matrix)

lemma pert_infmatrix_is_matrix: assumes as: "A ∈ matrix" 
  shows "pert_infmatrix A ∈ matrix"
  unfolding matrix_def
  unfolding mem_Collect_eq
proof (rule finite_imageD [of pert_inv])
  let ?B = "nonzero_positions (pert_infmatrix A)"
  show "finite (pert_inv ` ?B)"
  proof (rule finite_subset [of _ "{i. A (fst i) (snd i) ≠ 0}"])
    show "pert_inv` ?B ⊆ {i. A (fst i) (snd i) ≠ 0}"
      unfolding image_def
      unfolding pert_inv_def
      unfolding pert_infmatrix_def
      unfolding if_def
      unfolding nonzero_positions_def by force
    show "finite {i::nat × nat. A (fst i) (snd i) ≠ (0::'a)}"
      using as
      unfolding matrix_def
      unfolding nonzero_positions_def
      unfolding mem_Collect_eq .
  qed  
  show "inj_on pert_inv ?B"
    unfolding inj_on_def
    unfolding pert_inv_def by auto
qed

lemma pert_infmatrix_twice [simp]:
  shows "pert_infmatrix (pert_infmatrix A) = (Rep_matrix 0)"
  unfolding pert_infmatrix_def
  by (rule ext, rule ext, auto simp add: Rep_matrix_inject) arith

text{*We will use the name @{term "pert_matrix1"} to define the 
  perturbation in this context, leaving thus the name 
  @{term "pert_matrix"} to be used inside of the instance 
  @{text "matrix::diff_group_add_pert"}.*}

definition pert_matrix1 :: "'a::lordered_ring matrix => 'a matrix"
  where pert_matrix1_def: "pert_matrix1 == Abs_matrix o pert_infmatrix o Rep_matrix"

lemma combine_infmatrix_pert_infmatrix_matrix[simp]:
  "f 0 0 = 0 ==> combine_infmatrix f (pert_infmatrix (Rep_matrix A)) 
  (pert_infmatrix (Rep_matrix B)) ∈ matrix"
  unfolding matrix_def
  using finite_subset[of _ "(nonzero_positions (pert_infmatrix (Rep_matrix A))) 
    ∪ (nonzero_positions (pert_infmatrix (Rep_matrix B)))"] by simp_all

lemma pert_infmatrix_combine_infmatrix_matrix[simp]: 
  "f 0 0 = 0 ==> pert_infmatrix (combine_infmatrix f (Rep_matrix A) 
  (Rep_matrix B)) ∈ matrix"
proof (unfold matrix_def, simp, rule finite_imageD [of pert_inv])
  let ?B = "nonzero_positions (pert_infmatrix (combine_infmatrix f 
    (Rep_matrix A) (Rep_matrix B)))"
  assume f_0: "f (0::'b) (0::'c) = (0::'a)"
  show "finite (pert_inv ` ?B)"
  proof -
    have "pert_inv` ?B ⊆ {i. f (Rep_matrix A (fst i) (snd i)) 
      (Rep_matrix B (fst i) (snd i)) ≠ 0}"
      unfolding pert_infmatrix_def 
      unfolding combine_infmatrix_def 
      unfolding image_def nonzero_positions_def 
      unfolding pert_inv_def if_def by force
    with combine_infmatrix_matrix [of f] f_0 show ?thesis
      unfolding matrix_def combine_infmatrix_def nonzero_positions_def
      by (simp add: finite_subset)
  qed
  show "inj_on pert_inv ?B" unfolding inj_on_def pert_inv_def by auto
qed

lemma combine_diff_pert_matrix[simp]: 
  "f 0 0 = 0 ==> (combine_infmatrix f (diff_infmatrix 
  (combine_infmatrix f (Rep_matrix A) (Rep_matrix B)))
  (pert_infmatrix (combine_infmatrix f (Rep_matrix A) (Rep_matrix B)))) ∈ matrix"
  using diff_infmatrix_combine_infmatrix_matrix [of f]
  using pert_infmatrix_combine_infmatrix_matrix [of f]
  unfolding matrix_def
  by (simp add: finite_subset [of _ "(nonzero_positions 
    (diff_infmatrix (combine_infmatrix f (Rep_matrix A) (Rep_matrix B)))) 
    ∪ (nonzero_positions (pert_infmatrix (combine_infmatrix f (Rep_matrix A) (Rep_matrix B))))"])

lemma combine_diff_pert_matrix_1 [simp]: 
  "f 0 0 = 0 ==> (combine_infmatrix f (diff_infmatrix (Rep_matrix A))
  (pert_infmatrix (Rep_matrix A))) ∈ matrix"
  unfolding matrix_def
  by (simp add: finite_subset [of _ "(nonzero_positions (diff_infmatrix (Rep_matrix A)))
    ∪ (nonzero_positions (pert_infmatrix (Rep_matrix A)))"])

lemma combine_diff_pert_matrix_2 [simp]:
  "f 0 0 = 0 ==> (combine_infmatrix f 
  (combine_infmatrix f (diff_infmatrix (Rep_matrix A)) 
  (pert_infmatrix (Rep_matrix A)))
  (combine_infmatrix f (diff_infmatrix (Rep_matrix B)) (pert_infmatrix (Rep_matrix B)))) ∈ matrix"
  using combine_diff_pert_matrix_1 [of f A] 
  using combine_diff_pert_matrix_1 [of f B]
  unfolding matrix_def
  by (simp add: finite_subset [of _ "(nonzero_positions (combine_infmatrix f 
    (diff_infmatrix (Rep_matrix A)) (pert_infmatrix (Rep_matrix A))))
    ∪ (nonzero_positions (combine_infmatrix f (diff_infmatrix (Rep_matrix B)) 
    (pert_infmatrix (Rep_matrix B))))"])

lemma combine_diff_pert_matrix_3 [simp]:
  "f 0 0 = 0 ==> (diff_infmatrix (combine_infmatrix f (diff_infmatrix (Rep_matrix A)) 
  (pert_infmatrix (Rep_matrix A)))) ∈ matrix"
proof (unfold matrix_def, simp, rule finite_imageD [of diff_inv])
  let ?B = "nonzero_positions (diff_infmatrix (combine_infmatrix f
    (diff_infmatrix (Rep_matrix A)) (pert_infmatrix (Rep_matrix A))))"
  assume f_0: "f (0::'b) (0::'b) = (0::'a)"
  show "inj_on diff_inv ?B" unfolding inj_on_def diff_inv_def by auto
  show "finite (diff_inv `?B)"
  proof -
    have "diff_inv` ?B ⊆ {i. combine_infmatrix f (diff_infmatrix (Rep_matrix A)) 
      (pert_infmatrix (Rep_matrix A)) (fst i) (snd i) ≠ 0}"
      unfolding diff_infmatrix_def 
      unfolding pert_infmatrix_def 
      unfolding combine_infmatrix_def 
      unfolding image_def nonzero_positions_def 
      unfolding diff_inv_def 
      unfolding if_def by force
    with combine_diff_pert_matrix_1 [of f] f_0
    show ?thesis
      unfolding matrix_def
      unfolding nonzero_positions_def by (simp add: finite_subset)
  qed
qed

lemma combine_diff_pert_matrix_4 [simp]:
  "f 0 0 = 0 ==> (pert_infmatrix (combine_infmatrix f
  (diff_infmatrix (Rep_matrix A))
  (pert_infmatrix (Rep_matrix A)))) ∈ matrix"
proof (unfold matrix_def, simp, rule finite_imageD [of pert_inv])
  let ?B = "nonzero_positions (pert_infmatrix (combine_infmatrix f
    (diff_infmatrix (Rep_matrix A))
    (pert_infmatrix (Rep_matrix A))))"
  assume f_0: "f (0::'b) (0::'b) = (0::'a)"
  show "inj_on pert_inv ?B" unfolding inj_on_def pert_inv_def by auto
  show "finite (pert_inv `?B)"
  proof -
    have "pert_inv` ?B ⊆ {i. combine_infmatrix f (diff_infmatrix (Rep_matrix A)) 
      (pert_infmatrix (Rep_matrix A)) (fst i) (snd i) ≠ 0}"
      unfolding diff_infmatrix_def
      unfolding pert_infmatrix_def
      unfolding combine_infmatrix_def
      unfolding image_def
      unfolding nonzero_positions_def
      unfolding pert_inv_def
      unfolding if_def by force
    with combine_diff_pert_matrix_1 [of f] f_0 
    show ?thesis unfolding matrix_def nonzero_positions_def by (simp add: finite_subset)
  qed
qed

lemma combine_diff_pert_matrix_5 [simp]:
  "f 0 0 = 0 ==> (combine_infmatrix f (diff_infmatrix (combine_infmatrix f
  (diff_infmatrix (Rep_matrix A))
  (pert_infmatrix (Rep_matrix A))))
  (pert_infmatrix (combine_infmatrix
  f (diff_infmatrix (Rep_matrix A)) (pert_infmatrix (Rep_matrix A))))) ∈ matrix"
  using combine_diff_pert_matrix_3 [of f A] 
  using combine_diff_pert_matrix_4 [of f A]
  unfolding matrix_def
  by (simp add: finite_subset [of _
    "(nonzero_positions (diff_infmatrix (combine_infmatrix f 
    (diff_infmatrix (Rep_matrix A)) (pert_infmatrix (Rep_matrix A))))) 
    ∪ (nonzero_positions (pert_infmatrix (combine_infmatrix f 
    (diff_infmatrix (Rep_matrix A)) (pert_infmatrix (Rep_matrix A)))))"])

subsection{*Matrices as an instance of differential group with a perturbation.*}

lemma pert_matrix1_hom: shows "pert_matrix1 (A + B) = pert_matrix1 A + pert_matrix1 B"
  unfolding plus_matrix_def combine_matrix_def pert_matrix1_def
proof (simp add: expand_fun_eq)
  show "Abs_matrix (pert_infmatrix (combine_infmatrix op + (Rep_matrix A) 
    (Rep_matrix B))) =
    Abs_matrix (combine_infmatrix op + (pert_infmatrix (Rep_matrix A)) 
    (pert_infmatrix (Rep_matrix B)))"
  proof (rule Abs_matrix_inject2)
    show "combine_infmatrix op + (pert_infmatrix (Rep_matrix A)) 
      (pert_infmatrix (Rep_matrix B)) ∈ matrix"
      using combine_infmatrix_pert_infmatrix_matrix [of "op + :: 'a => 'a => 'a"]
      by simp
    show "pert_infmatrix (combine_infmatrix op + (Rep_matrix A)
      (Rep_matrix B)) ∈ matrix"
      using combine_infmatrix_pert_infmatrix_matrix [of "op + :: 'a => 'a => 'a"]
      by simp
    show " pert_infmatrix (combine_infmatrix op + (Rep_matrix A) (Rep_matrix B)) =
      combine_infmatrix op + (pert_infmatrix (Rep_matrix A)) (pert_infmatrix (Rep_matrix B))"
      unfolding combine_infmatrix_def pert_infmatrix_def
      apply (rule ext)+ by auto
  qed
qed

lemma pert_matrix1_twice: 
  shows "pert_matrix1 (pert_matrix1 A) = (0::('a::lordered_ring matrix))"
  unfolding pert_matrix1_def by simp

lemma diff_matrix1_pert_matrix1_is_zero: "diff_matrix1 (pert_matrix1 A) = 0"
  unfolding zero_matrix_def
  unfolding diff_matrix1_def
  unfolding pert_matrix1_def
  apply auto
proof -
  show "Abs_matrix (diff_infmatrix (pert_infmatrix (Rep_matrix A))) = 
    Abs_matrix (λj i. 0)"
  proof (rule Abs_matrix_inject2)
    show "(λ(j::nat) i::nat. 0::'a) ∈ matrix"
      unfolding zero_fun_def
      unfolding matrix_def
      unfolding nonzero_positions_def by auto
    show "diff_infmatrix (pert_infmatrix (Rep_matrix A)) ∈ matrix"
      using diff_infmatrix_is_matrix [OF pert_infmatrix_matrix [of A]] .
    show "diff_infmatrix (pert_infmatrix (Rep_matrix A)) = (λj i. 0)"
      unfolding pert_infmatrix_def
      unfolding diff_infmatrix_def
      apply (rule ext)+ by arith
  qed
qed

lemma pert_matrix1_diff_matrix1_is_zero: "pert_matrix1 (diff_matrix1 A) = 0"
  unfolding zero_matrix_def
  unfolding diff_matrix1_def
  unfolding pert_matrix1_def
  apply auto
proof -
  show "Abs_matrix (pert_infmatrix (diff_infmatrix (Rep_matrix A))) = 
    Abs_matrix (λj i. 0)"
  proof (rule Abs_matrix_inject2)
    show "(λ(j::nat) i::nat. 0::'a) ∈ matrix"
      unfolding zero_fun_def
      unfolding matrix_def
      unfolding nonzero_positions_def by auto
    show "pert_infmatrix (diff_infmatrix (Rep_matrix A)) ∈ matrix"
      using pert_infmatrix_is_matrix [OF diff_infmatrix_matrix [of A]] .
    show "pert_infmatrix (diff_infmatrix (Rep_matrix A)) = (λj i. 0)"
      unfolding pert_infmatrix_def
      unfolding diff_infmatrix_def
      apply (rule ext)+ by arith
  qed
qed

instantiation matrix :: (lordered_ring) diff_group_add_pert
begin

definition pert_matrix_def: "pert A == pert_matrix1 A"

instance
proof
  fix A B :: "'a matrix"
  show "δ (A + B) = δ A + δ B"
    unfolding pert_matrix_def using pert_matrix1_hom [of A B] by simp
next
  show "diff_group_add op - uminus (0::'a matrix) op + (λx::'a matrix. d x + δ x)"
  proof (intro_locales, unfold diff_group_add_axioms_def,
      intro conjI, rule allI, rule allI)
    fix A B :: "'a matrix"
    show "d (A + B) + δ (A + B) = d A + δ A + (d B + δ B)"
      unfolding diff_matrix_def unfolding diff_matrix1_hom [of A B]
      unfolding pert_matrix_def unfolding pert_matrix1_hom [of A B] by simp
  next
    show "(λA::'a matrix. d A + δ A) o (λA::'a matrix. d A + δ A) = (λA::'a matrix. 0)"
    proof (simp add: expand_fun_eq, rule allI)
      fix A :: "'a matrix"
      show "d (d A + δ A) + δ (d A + δ A) = 0"
        unfolding diff_matrix_def
        unfolding diff_matrix1_hom
        unfolding pert_matrix_def
        unfolding pert_matrix1_hom
        unfolding diff_matrix1_twice [of A]
        unfolding pert_matrix1_twice [of A]
        unfolding diff_matrix1_pert_matrix1_is_zero
        unfolding pert_matrix1_diff_matrix1_is_zero by simp
    qed
  qed
qed

end

subsection{*Definition of the homotopy operator $h$.*}

definition hom_oper_infmatrix :: "'a::{zero} infmatrix => 'a infmatrix"
  where hom_oper_infmatrix_def : "hom_oper_infmatrix A =
  (%i::nat. %j::nat. if (j = 0) then 0 else (if ((i + j) mod (2::nat) = 0) then (0::'a)
                                                                   else (A i (j - 1))))"

definition hom_oper_inv :: "(nat × nat) => (nat × nat)"
  where hom_oper_inv_def : "hom_oper_inv pos = (fst pos, (snd pos) - 1)"

lemma hom_oper_infmatrix_finite[simp]: 
  shows "finite (nonzero_positions (hom_oper_infmatrix (Rep_matrix x)))"
proof (rule finite_imageD [of hom_oper_inv])
  let ?B = "nonzero_positions (hom_oper_infmatrix (Rep_matrix x))"
  show "finite (hom_oper_inv ` ?B)"
  proof (rule finite_subset [of _ "{i. Rep_matrix x (fst i) (snd i) ≠ 0}"])
    show "hom_oper_inv` ?B ⊆ {i. Rep_matrix x (fst i) (snd i) ≠ 0}"
      unfolding hom_oper_infmatrix_def 
      unfolding image_def 
      unfolding nonzero_positions_def 
      unfolding hom_oper_inv_def 
      unfolding if_def by force
    show "finite {i. Rep_matrix x (fst i) (snd i) ≠ 0}"
      using finite_nonzero_positions [of x]
      unfolding nonzero_positions_def [of "Rep_matrix x"] .
  qed
  show "inj_on hom_oper_inv ?B"
  proof (unfold inj_on_def hom_oper_inv_def, auto)
    fix a b c :: nat
    assume "(a, b) ∈ nonzero_positions (hom_oper_infmatrix (Rep_matrix x))"
    and "(a, c) ∈ nonzero_positions (hom_oper_infmatrix (Rep_matrix x))"
    then have b_ge_1: "1 ≤ b" and c_ge_1: "1 ≤ c"
      unfolding nonzero_positions_def
      unfolding hom_oper_infmatrix_def
      unfolding if_def by force+
    assume "b - Suc (0::nat) = c - Suc (0::nat)"
    with eq_diff_iff [OF b_ge_1 c_ge_1] show "b = c" by simp
  qed
qed
 
lemma hom_oper_infmatrix_matrix:
  shows "(hom_oper_infmatrix (Rep_matrix (x::'a::zero matrix))) ∈ matrix"
  by (unfold matrix_def) (simp add: hom_oper_infmatrix_finite)

lemma hom_oper_infmatrix_closed[simp]: 
  "Rep_matrix (Abs_matrix (hom_oper_infmatrix (Rep_matrix x))) = hom_oper_infmatrix (Rep_matrix x)"
  using Abs_matrix_inverse [OF hom_oper_infmatrix_matrix [of x]] .

lemma hom_oper_infmatrix_is_matrix: assumes as: "A ∈ matrix"
  shows "hom_oper_infmatrix A ∈ matrix"
  unfolding matrix_def
  unfolding mem_Collect_eq
proof (rule finite_imageD [of hom_oper_inv])
  let ?B = "nonzero_positions (hom_oper_infmatrix A)"
  show "finite (hom_oper_inv ` ?B)"
  proof (rule finite_subset [of _ "{i. A (fst i) (snd i) ≠ 0}"])
    show "hom_oper_inv` ?B ⊆ {i. A (fst i) (snd i) ≠ 0}"
      unfolding image_def
      unfolding hom_oper_inv_def
      unfolding hom_oper_infmatrix_def
      unfolding if_def
      unfolding nonzero_positions_def by force
    show "finite {i::nat × nat. A (fst i) (snd i) ≠ (0::'a)}"
      using as
      unfolding matrix_def
      unfolding nonzero_positions_def
      unfolding mem_Collect_eq .
  qed
  show "inj_on hom_oper_inv ?B"
  proof (unfold inj_on_def hom_oper_inv_def, auto)
    fix a b c :: nat
    assume "(a, b) ∈ nonzero_positions (hom_oper_infmatrix A)"
    and "(a, c) ∈ nonzero_positions (hom_oper_infmatrix A)"
    then have b_ge_1: "1 ≤ b" and c_ge_1: "1 ≤ c"
      unfolding nonzero_positions_def
      unfolding hom_oper_infmatrix_def
      unfolding if_def by force+
    assume "b - Suc (0::nat) = c - Suc (0::nat)"
    with eq_diff_iff [OF b_ge_1 c_ge_1] show "b = c" by simp
  qed
qed

definition  hom_oper_matrix1 :: "'a::lordered_ring matrix => 'a matrix"
  where hom_oper_matrix1_def:
  "hom_oper_matrix1 = Abs_matrix o hom_oper_infmatrix o Rep_matrix"

lemma hom_oper_infmatrix_twice [simp]: 
  "hom_oper_infmatrix (hom_oper_infmatrix A) = (Rep_matrix 0)"
  unfolding hom_oper_infmatrix_def
  apply (rule ext)+
  apply (auto simp add: Rep_matrix_inject) by arith

lemma combine_infmatrix_hom_oper_infmatrix_matrix[simp]: 
  "f 0 0 = 0 ==> combine_infmatrix f (hom_oper_infmatrix (Rep_matrix A)) 
  (hom_oper_infmatrix (Rep_matrix B)) ∈ matrix"
  unfolding matrix_def
  using finite_subset[of _ "(nonzero_positions (hom_oper_infmatrix (Rep_matrix A))) 
    ∪ (nonzero_positions (hom_oper_infmatrix (Rep_matrix B)))"]
  by simp

lemma hom_oper_infmatrix_combine_infmatrix_matrix[simp]: 
  "f 0 0 = 0 ==> hom_oper_infmatrix (combine_infmatrix f (Rep_matrix A) (Rep_matrix B)) ∈ matrix"
  proof (unfold matrix_def, simp, rule finite_imageD [of hom_oper_inv])
  let ?B = "nonzero_positions (hom_oper_infmatrix (combine_infmatrix f (Rep_matrix A) 
    (Rep_matrix B)))"
  assume f_0: "f (0::'b) (0::'c) = (0::'a)"
  show "finite (hom_oper_inv ` ?B)"
  proof (rule finite_subset [of _ "{i. f (Rep_matrix A (fst i) (snd i))
      (Rep_matrix B (fst i) (snd i)) ≠ 0}"])
    show "hom_oper_inv` ?B ⊆ {i. f (Rep_matrix A (fst i) (snd i)) (Rep_matrix B (fst i) (snd i)) ≠ 0}"
      unfolding hom_oper_infmatrix_def 
      unfolding combine_infmatrix_def 
      unfolding image_def 
      unfolding nonzero_positions_def
      unfolding hom_oper_inv_def
      unfolding if_def by force
    show "finite {i. f (Rep_matrix A (fst i) (snd i))
      (Rep_matrix B (fst i) (snd i)) ≠ 0}"
      using combine_infmatrix_matrix [of f] f_0
      unfolding matrix_def
      unfolding combine_infmatrix_def 
      unfolding nonzero_positions_def
      unfolding mem_Collect_eq .
  qed
  show "inj_on hom_oper_inv ?B"
  proof (unfold inj_on_def hom_oper_inv_def, auto)
    fix a b c :: nat
    assume "(a, b) ∈ ?B" and "(a, c) ∈ ?B"
    then have  b_ge_1: "1 ≤ b" and c_ge_1: "1 ≤ c"
      unfolding nonzero_positions_def 
      unfolding hom_oper_infmatrix_def
      unfolding  if_def by force+
    assume "b - Suc (0::nat) = c - Suc (0::nat)"
    with eq_diff_iff [OF b_ge_1 c_ge_1] show "b = c" by simp
  qed
qed

subsection{*Matrices as an instance of differential group with a homotopy operator.*}

text{*Matrices with the given operations for differential, 
  perturbation and homotopy operator are an instance of the 
  type class representing differential groups with a perturbation and a homotpy operator:*}

lemma hom_oper_matrix1_hom: 
  "hom_oper_matrix1 (A + B) = hom_oper_matrix1 A + hom_oper_matrix1 B"
  unfolding plus_matrix_def
  unfolding combine_matrix_def
  unfolding hom_oper_matrix1_def 
  apply simp
proof -
  show "Abs_matrix (hom_oper_infmatrix (combine_infmatrix op + (Rep_matrix A) 
    (Rep_matrix B))) =
    Abs_matrix (combine_infmatrix op + (hom_oper_infmatrix (Rep_matrix A)) 
    (hom_oper_infmatrix (Rep_matrix B)))"
  proof (rule Abs_matrix_inject2)
    show "combine_infmatrix op + (hom_oper_infmatrix (Rep_matrix A)) 
      (hom_oper_infmatrix (Rep_matrix B)) ∈ matrix"
      using combine_infmatrix_hom_oper_infmatrix_matrix [of "op + :: 'a => 'a => 'a"]
      by simp
    show "hom_oper_infmatrix (combine_infmatrix op + (Rep_matrix A) (Rep_matrix B)) ∈ matrix"
      using hom_oper_infmatrix_combine_infmatrix_matrix [of "op + :: 'a => 'a => 'a"]
      by simp
    show "hom_oper_infmatrix (combine_infmatrix op + (Rep_matrix A) (Rep_matrix B)) =
      combine_infmatrix op + (hom_oper_infmatrix (Rep_matrix A)) 
      (hom_oper_infmatrix (Rep_matrix B))"
      unfolding combine_infmatrix_def 
      unfolding hom_oper_infmatrix_def by (rule ext)+ simp
  qed
qed

lemma hom_oper_matrix1_twice: 
  shows "hom_oper_matrix1 (hom_oper_matrix1 A) = (0::('a::lordered_ring matrix))"
  unfolding hom_oper_matrix1_def by simp

instantiation matrix :: (lordered_ring) diff_group_add_pert_hom
begin

definition hom_oper_matrix_def: "hom_oper A == hom_oper_matrix1 A"

instance
proof
  fix A B :: "'a matrix"
  show "hom_oper o hom_oper = (λx::'a matrix. 0)"
    apply (rule ext)
    unfolding o_apply
    unfolding hom_oper_matrix_def
    using hom_oper_matrix1_twice by auto
next
  fix A B:: "'a matrix"
  show "h (A + B) = h A + h B"
    unfolding hom_oper_matrix_def
    using hom_oper_matrix1_hom [of A B].
qed

end

subsection{*Local nilpotency condition of the previous definitions.*}

lemma hom_oper_closed: 
  shows "Rep_matrix o Abs_matrix o hom_oper_infmatrix o Rep_matrix 
  = hom_oper_infmatrix o Rep_matrix"
  by (simp add: expand_fun_eq)

lemma pert_infmatrix_hom_oper_infmatrix_finite[simp]: 
  shows "finite (nonzero_positions (pert_infmatrix (hom_oper_infmatrix (Rep_matrix x))))"
proof (rule finite_imageD [of "pert_inv"])
  let ?B = "nonzero_positions (pert_infmatrix (hom_oper_infmatrix (Rep_matrix x)))"
  show "finite ((pert_inv) ` ?B)"
  proof -
    have "(pert_inv)` ?B ⊆ {i. hom_oper_infmatrix (Rep_matrix x) (fst i) (snd i) ≠ 0}"
      unfolding pert_infmatrix_def 
      unfolding image_def
      unfolding nonzero_positions_def 
      unfolding pert_inv_def 
      unfolding if_def by force
    from finite_subset [OF this] hom_oper_infmatrix_finite [of x] show ?thesis
      unfolding nonzero_positions_def [of "(hom_oper_infmatrix (Rep_matrix x))"]
      by simp
  qed
  show "inj_on pert_inv ?B"
    unfolding inj_on_def
    unfolding pert_inv_def by auto
qed

lemma pert_infmatrix_hom_oper_infmatrix_matrix:
  shows "(pert_infmatrix (hom_oper_infmatrix (Rep_matrix (x::'a::zero matrix)))) 
  ∈ matrix"
  unfolding matrix_def
  unfolding mem_Collect_eq
  using pert_infmatrix_hom_oper_infmatrix_finite .

lemma pert_infmatrix_hom_oper_infmatrix_closed[simp]:
  shows "Rep_matrix (Abs_matrix (pert_infmatrix (hom_oper_infmatrix (Rep_matrix A)))) = 
  pert_infmatrix (hom_oper_infmatrix (Rep_matrix A))"
  using Abs_matrix_inverse [OF  pert_infmatrix_hom_oper_infmatrix_matrix [of A]] .

lemma hom_oper_infmatrix_pert_infmatrix_finite[simp]: 
  shows "finite (nonzero_positions (hom_oper_infmatrix (pert_infmatrix (Rep_matrix x))))"
proof (rule finite_imageD [of "hom_oper_inv"])
  let ?B = "nonzero_positions (hom_oper_infmatrix (pert_infmatrix (Rep_matrix x)))"
  show "finite ((hom_oper_inv) ` ?B)"
  proof -
    have "(hom_oper_inv)` ?B ⊆ {i. pert_infmatrix (Rep_matrix x) (fst i) (snd i) ≠ 0}"
      unfolding hom_oper_infmatrix_def 
      unfolding image_def
      unfolding nonzero_positions_def
      unfolding hom_oper_inv_def
      unfolding if_def by force
    from finite_subset [OF this] pert_infmatrix_finite [of x]
    show ?thesis
      unfolding nonzero_positions_def [of "(pert_infmatrix (Rep_matrix x))"]
      by simp
  qed
  show "inj_on hom_oper_inv ?B"
  proof (unfold inj_on_def hom_oper_inv_def, auto)
    fix a b c :: nat
    assume "(a, b) ∈ nonzero_positions (hom_oper_infmatrix (pert_infmatrix (Rep_matrix x)))"
      and "(a, c) ∈ nonzero_positions (hom_oper_infmatrix (pert_infmatrix (Rep_matrix x)))"
    then have b_ge_1: "1 ≤ b" and c_ge_1: "1 ≤ c"
      unfolding nonzero_positions_def
      unfolding pert_infmatrix_def
      unfolding hom_oper_infmatrix_def
      unfolding if_def by force+
    assume "b - Suc (0::nat) = c - Suc (0::nat)"
    with eq_diff_iff [OF b_ge_1 c_ge_1] show "b = c" by simp
  qed
qed

lemma hom_oper_infmatrix_pert_infmatrix_matrix:
  shows "(hom_oper_infmatrix (pert_infmatrix (Rep_matrix (x::'a::zero matrix)))) 
  ∈ matrix"
  unfolding matrix_def
  unfolding mem_Collect_eq
  using hom_oper_infmatrix_pert_infmatrix_finite .

lemma hom_oper_infmatrix_pert_infmatrix_closed[simp]:
  shows "Rep_matrix (Abs_matrix (hom_oper_infmatrix (pert_infmatrix (Rep_matrix A)))) = 
  hom_oper_infmatrix (pert_infmatrix (Rep_matrix A))"
  using Abs_matrix_inverse [OF  hom_oper_infmatrix_pert_infmatrix_matrix [of A]] .

lemma f_1_n: shows "f ((f^n) a) = (f^(n + 1)) a"
  using sym [OF funpow_add [of f 1 n]] by auto

text{*This is the crucial lemma to prove the local nilpotency condition; 
  the result states that there is 
  no infinite strictly descending chain of natural numbers. 
  Applied to matrices, it states that there is 
  no infinite descending path across a matrix of  finite dimension, and thus, 
  being @{term [locale=diff_group_add_pert_hom] "α = (pert o hom_oper)"} strictly
  decreasing with repect to the dimension of matrices, the local nilpotency condition holds.*}

lemma infinite_descending_chain_false:
  fixes g :: "nat => nat"
  assumes n_inf_decr: "!!n. g (Suc n) < g n" shows "False"
proof -
  obtain a where g0_finite: "a = g (0::nat)" by simp
  have less_relat: "!!n::nat. g (Suc n) < a - n"
  proof (induct_tac n)
    fix n::nat
    show "g (Suc 0) < a - 0" unfolding g0_finite using n_inf_decr [of 0] by simp
  next
    fix n::nat assume hypo: "g (Suc n) < a - n"
    show "g (Suc (Suc n)) < a - (Suc n)" 
      using hypo n_inf_decr [of "Suc n"] by arith
  qed
  from less_relat [of a] have "g (Suc a) < (0::nat)" by arith
  then show False by arith
qed

text{*Making use of the previous result, if the number of rows is strictly decreasing,
  finally matrix @{term "0::'a::zero matrix"} is reached.*}

lemma P_strict_decr:
  fixes f :: "'a::lordered_ring matrix => 'a matrix"
  and P :: "'a matrix => nat"
  assumes P_decr: "!!A. A ≠ 0 ==> (P (f A) < P A)" and P_0: "P 0 = 0"
  shows "∃n::nat. P ((f^n) A) = 0"
proof -
  have decr_step: "!!n. ((f^n) A) ≠ 0 ==> P ((f^(n+1)) A) < P ((f^n) A)"
  proof -
    fix n::nat
    show "((f^n) A) ≠ 0 ==> P ((f^(n+1)) A) < P ((f^n) A)"
    proof (induct n)
      case 0
      assume "(f^(0::nat)) A ≠ 0"
      then have A_nz: "A ≠ 0" 
        unfolding funpow.simps (1) [of f]
        unfolding id_apply .
      show ?case using P_decr [OF A_nz] by simp
    next
      case Suc fix n :: nat
      assume hypo: "(f^n) A ≠ 0 ==> P ((f^(n+1)) A) < P ((f^n) A)"
        and not_zero: "((f^(Suc n)) A) ≠ 0"
      show "P ((f^((Suc n)+1)) A) < P ((f^(Suc n)) A)"
        unfolding sym [OF f_1_n [of f "Suc m" A]]
        using P_decr [OF not_zero] by simp
    qed
  qed
  let ?P = "P A"
  have finite: "finite {..?P}" using finite_atMost [of "P A"] .
  from finite decr_step show ?thesis
  proof (cases "∃n. ((f^n) A) = 0")
    case True show ?thesis
    proof -
      from True obtain n where fn_0: "((f^n) A) = 0" by auto
      show ?thesis apply (rule exI [of _ n])
        unfolding fn_0 unfolding P_0 ..
    qed
  next
    case False show ?thesis
    proof (rule ccontr)
      let ?g = "(λn::nat. P ((f^n) A))"
      have "!!n::nat. P ((f^(n+1)) A) < P ((f^n) A)"
        using decr_step and False by simp
      then have infinite_descending_chain: "!!n::nat. ?g (Suc n) < ?g n" by simp
      with infinite_descending_chain_false [of ?g] show False by simp
    qed
  qed
qed

lemma nrows_strict_decr:
  fixes f :: "'a::lordered_ring  matrix => 'a matrix"
  assumes nrows_decr: "!!A. A ≠ 0 ==> (nrows (f A) < nrows A)"
  shows "∃n::nat. nrows ( (f^n) A) = 0"
proof (rule P_strict_decr [of nrows f A])
  show "!!A. A ≠ 0 ==> (nrows (f A) < nrows A)" using nrows_decr .
  show "nrows 0 = 0" by simp
qed

lemma ncols_strict_decr:
  fixes f :: "'a::lordered_ring  matrix => 'a matrix"
  assumes ncols_decr: "!!A. A ≠ 0 ==> (ncols (f A) < ncols A)"
  shows "∃n::nat. ncols ( (f^n) A) = 0"
proof (rule P_strict_decr [of ncols f A])
  show "!!A. A ≠ 0 ==> (ncols (f A) < ncols A)" using ncols_decr .
  show "ncols 0 = 0" by simp
qed

lemma nonzero_empty_set:
  shows "nonzero_positions (Rep_matrix (0::'a::lordered_ring matrix)) = {}"
  unfolding nonzero_positions_def by simp

lemma Rep_matrix_inject2: 
  assumes R_A_B: "Rep_matrix A = Rep_matrix B"
  shows "A = B"
  using Rep_matrix_inject [of A B] R_A_B ..

lemma nonzero_imp_zero_matrix:
  assumes nz_p: "nonzero_positions (Rep_matrix (A::'a::lordered_ring matrix)) = {}"
  shows "A = 0"
proof (rule Rep_matrix_inject2)
  show "Rep_matrix A = Rep_matrix 0"
  proof -
    from nz_p have "∀a b. Rep_matrix A a b = 0"
      unfolding nonzero_positions_def by simp
    then show "Rep_matrix A = Rep_matrix 0" 
      unfolding expand_fun_eq
      unfolding Rep_zero_matrix_def .
  qed
qed

lemma nrows_zero_impl_zero:
  assumes nrows_0: "nrows (A::'a::lordered_ring matrix) = (0::nat)"
  shows "A = 0"
proof (rule nonzero_imp_zero_matrix)
  show "nonzero_positions (Rep_matrix A) = {}"
  proof (rule ccontr)
    assume ne: "nonzero_positions (Rep_matrix A) ≠ {}"
    then have "nrows A > 0"
      unfolding nrows_def by simp
    with nrows_0 show False by arith
  qed
qed

lemma ncols_zero_impl_zero:
  assumes ncols_0: "ncols (A::'a::lordered_ring matrix) = (0::nat)"
  shows "A = 0"
proof (rule nonzero_imp_zero_matrix)
  show "nonzero_positions (Rep_matrix A) = {}"
  proof (rule ccontr)
    assume ne: "nonzero_positions (Rep_matrix A) ≠ {}"
    then have "ncols A > 0"
      unfolding ncols_def by simp
    with ncols_0 show False by arith
  qed
qed

lemma not_zero_impl_exist: 
  assumes A_not_null: "(A::'a::lordered_ring matrix) ≠ 0" 
  shows "∃m n::nat. Rep_matrix A m n ≠ 0"
proof (rule ccontr)
  assume neg: "¬ (∃m n::nat. Rep_matrix A m n ≠ 0)"
  then have "∀m n. Rep_matrix A m n = 0" by auto
  with nonzero_imp_zero_matrix [of A] nonzero_positions_def 
  have "A = 0" by auto
  with A_not_null show False by simp
qed

corollary not_zero_impl_nonzero_pos: 
  assumes A_not_null: "(A::'a::lordered_ring matrix) ≠ 0" 
  shows "nonzero_positions (Rep_matrix A) ≠ {}"
  using not_zero_impl_exist [OF A_not_null] 
  unfolding nonzero_positions_def [of "Rep_matrix A"] by auto

corollary not_zero_impl_nrows_g_0: 
  assumes A_not_null: "(A::'a::lordered_ring matrix) ≠ 0" 
  shows "nrows A > 0"
  using not_zero_impl_exist [OF A_not_null] 
  unfolding nrows_def
  unfolding nonzero_positions_def by auto

corollary not_zero_impl_ncols_g_0: 
  assumes A_not_null: "(A::'a::lordered_ring matrix) ≠ 0" 
  shows "ncols A > 0"
  using not_zero_impl_exist [OF A_not_null] 
  unfolding ncols_def
  unfolding nonzero_positions_def by auto

lemma hom_oper_nonzero_positions:
  assumes a_b_1: "(a, b + 1) ∈ nonzero_positions (Rep_matrix A)" 
  and b_n_0: "(0::nat) < b" 
  and ab_even: "(a + b) mod 2 ≠ (0::nat)" 
  shows "(a, b + 2) ∈ nonzero_positions (hom_oper_infmatrix (Rep_matrix A))"
  using a_b_1 b_n_0 ab_even
  unfolding hom_oper_infmatrix_def
  unfolding nonzero_positions_def by auto

lemma pert_nonzero_positions: 
  assumes a_b_1: "(a + 1, b) ∈ nonzero_positions (Rep_matrix A)"
  and ab_pair: "(a + b) mod 2 = (0::nat)"
  shows "(a, b) ∈ nonzero_positions (pert_infmatrix (Rep_matrix A))"
  using a_b_1 ab_pair
  unfolding pert_infmatrix_def
  unfolding nonzero_positions_def by auto

lemma pert_nonzero_incr:
  assumes ab_nonzero: "(a, b) ∈ nonzero_positions (pert_infmatrix (Rep_matrix A))" 
  shows "(a + 1, b) ∈ nonzero_positions (Rep_matrix A)"
proof (rule ccontr)
  assume contr: "(a + (1::nat), b) ∉ nonzero_positions (Rep_matrix A)"
  then have Rep_m: "Rep_matrix A (a + 1) b = 0"
    unfolding nonzero_positions_def unfolding mem_Collect_eq by simp
  then have "pert_infmatrix (Rep_matrix A) a b = 0"
    unfolding pert_infmatrix_def by simp
  with ab_nonzero show False 
    unfolding nonzero_positions_def 
    unfolding mem_Collect_eq by simp
qed

lemma pert_hom_oper_nonzero_ncols_g_1:
  assumes ab_nonzero: "(a, b) ∈ nonzero_positions 
  (pert_infmatrix (hom_oper_infmatrix (Rep_matrix A)))" 
  shows "1 ≤ b"
proof (rule ccontr)
  assume contr: "¬ (1::nat) ≤ b" then have b_0: "b = 0" by arith
  have Rep_m: "(pert_infmatrix (hom_oper_infmatrix (Rep_matrix A))) a b = 0"
    unfolding hom_oper_infmatrix_def
    unfolding pert_infmatrix_def using b_0 by auto
  with ab_nonzero show False unfolding nonzero_positions_def unfolding mem_Collect_eq 
    by simp
qed

lemma pert_hom_oper_nonzero_incr_rows:
  assumes ab_nonzero: "(a, b) ∈ nonzero_positions 
  (pert_infmatrix (hom_oper_infmatrix (Rep_matrix A)))" 
  shows "(a + 1, b - 1) ∈ nonzero_positions (Rep_matrix A)"
proof (rule ccontr)
  assume contr: "(a + 1, b - 1) ∉ nonzero_positions (Rep_matrix A)"
  then have Rep_m: "Rep_matrix A (a + 1) (b - 1) = 0" 
    unfolding nonzero_positions_def by simp
  have "pert_infmatrix (hom_oper_infmatrix (Rep_matrix A)) a b = 0"
    unfolding hom_oper_infmatrix_def 
    unfolding pert_infmatrix_def using Rep_m by auto
  with ab_nonzero show False unfolding nonzero_positions_def unfolding mem_Collect_eq
    by simp
qed

lemma hom_oper_pert_nonzero_incr_rows:
  assumes ab_nonzero: "(a, b) ∈ nonzero_positions 
  (hom_oper_infmatrix (pert_infmatrix (Rep_matrix A)))" 
  shows "(a + 1, b - 1) ∈ nonzero_positions (Rep_matrix A)"
proof (rule ccontr)
  assume contr: "(a + 1, b - 1) ∉ nonzero_positions (Rep_matrix A)"
  then have Rep_m: "Rep_matrix A (a + 1) (b - 1) = 0"
    unfolding nonzero_positions_def by simp
  have "hom_oper_infmatrix (pert_infmatrix (Rep_matrix A)) a b = 0"
    unfolding hom_oper_infmatrix_def
    unfolding pert_infmatrix_def using Rep_m by auto
  with ab_nonzero 
  show False 
    unfolding nonzero_positions_def 
    unfolding mem_Collect_eq
    by simp
qed

lemma Max_A_B: 
  assumes exist: "∃m>Max B. m ∈ A" 
  and fin_A: "finite A" and fin_B: "finite B"shows "Max B < Max A"
proof -
  from exist obtain m where m_prop: "m ∈ A ∧ (m>Max B)" by auto
  from m_prop Max_ge [OF fin_A, of m] have "m ≤ Max A" by auto
  with m_prop show "Max B < Max A" by auto
qed

lemma nrows_pert_decre:
  assumes A_not_null: "(A::'a::lordered_ring matrix) ≠ 0"
  shows "nrows ( (- (pert_matrix1)) A) < nrows A"
  (is "nrows ((- ?f) A) < nrows A")
proof (cases "(- ?f) A = 0")
  case True
  have fA_0: "nrows ((- ?f) A) = (0::nat)" 
    unfolding True 
    using zero_matrix_def_nrows .
  with not_zero_impl_nrows_g_0 [OF A_not_null] show ?thesis by arith
next
  case False
  show "nrows ((- ?f) A) < nrows A"
  proof -
    from not_zero_impl_nonzero_pos [OF A_not_null]
    have nrows_A: "nrows A = Suc (Max (fst ` nonzero_positions (Rep_matrix A)))"
      unfolding nrows_def by simp
    from not_zero_impl_nonzero_pos [OF False] 
    have "nrows ((- ?f) A) = Suc (Max (fst ` nonzero_positions (Rep_matrix ((- ?f) A))))"
      unfolding nrows_def by simp
    also from nonzero_positions_minus_f [of ?f A] 
    have "… = Suc (Max (fst ` nonzero_positions (Rep_matrix (?f A))))"
      by simp
    also have "… = Suc (Max (fst ` nonzero_positions ((pert_infmatrix o Rep_matrix) A)))"
      unfolding pert_matrix1_def by simp
    finally
    have nrows_fA: "nrows ((- ?f) A) = 
      Suc (Max (fst ` nonzero_positions ((pert_infmatrix o Rep_matrix) A)))" by simp
    have Max_l: "Max (fst ` nonzero_positions ((pert_infmatrix o Rep_matrix) A))
      < Max (fst ` nonzero_positions (Rep_matrix A))"
    proof (rule Max_A_B)
      let ?n = "Max (fst ` nonzero_positions ((pert_infmatrix o Rep_matrix) A))"
      show "∃m>?n .(m ∈ (fst ` nonzero_positions (Rep_matrix A)))"
      proof (rule exI [of _ "Suc ?n"], intro conjI, simp)
        have "nonzero_positions (Rep_matrix ((- ?f) A)) ≠ {}"
          using not_zero_impl_nonzero_pos [OF False] .
        with nonzero_positions_minus_f [of ?f A]
        have non_empty: "nonzero_positions ((pert_infmatrix o Rep_matrix) A) ≠ {}" 
          unfolding pert_matrix1_def by simp
        from Max_in [OF finite_imageI [OF pert_infmatrix_finite, of "fst" A]] non_empty
        have Max_in_set: "?n ∈ (fst ` nonzero_positions ((pert_infmatrix o Rep_matrix) A))"
          unfolding image_def by auto
        from Max_in_set obtain a 
          where A_a_n_0: "(pert_infmatrix (Rep_matrix A)) ?n a ≠ 0"
          unfolding nonzero_positions_def image_def by auto
        with pert_nonzero_incr [of ?n a] 
        have "(Suc ?n, a) ∈ nonzero_positions (Rep_matrix A)"
          unfolding nonzero_positions_def image_def by auto
        then show "Suc ?n ∈ fst ` nonzero_positions (Rep_matrix A)" 
          unfolding image_def [of fst] by force
      qed
      show "finite (fst ` nonzero_positions (Rep_matrix A))" 
        using finite_imageI [OF finite_nonzero_positions [of A], of fst] .
      show "finite (fst ` nonzero_positions ((pert_infmatrix o Rep_matrix) A))"
        using finite_imageI [OF pert_infmatrix_finite [of A], of fst] 
        unfolding o_apply .
    qed
    with nrows_A nrows_fA show ?thesis by simp
  qed
qed

lemma nrows_pert_hom_oper_decre:
  assumes A_not_null: "(A::'a::lordered_ring matrix) ≠ 0"
  shows "nrows ((- (pert_matrix1 o hom_oper_matrix1)) A) < nrows A"
  (is "nrows ((- ?f) A) < nrows A")
proof (cases "(- ?f) A = 0")
  case True
  have fA_0: "nrows ((- ?f) A) = (0::nat)" 
    unfolding True
    using zero_matrix_def_nrows .
  with not_zero_impl_nrows_g_0 [OF A_not_null]
  show ?thesis by arith
next
  case False
  show "nrows ((- ?f) A) < nrows A"
  proof -
    have nrows_A: "nrows A = Suc (Max (fst ` nonzero_positions (Rep_matrix A)))"
      using not_zero_impl_nonzero_pos [OF A_not_null]
      unfolding nrows_def by simp
    have "nrows ((- ?f) A) = Suc (Max (fst ` nonzero_positions (Rep_matrix ((- ?f) A))))"
      using not_zero_impl_nonzero_pos [OF False]
      unfolding nrows_def by simp
    also have "… = Suc (Max (fst ` nonzero_positions (Rep_matrix (?f A))))" 
      unfolding sym [OF nonzero_positions_minus_f [of ?f A]] ..
    also have "… = Suc (Max (fst ` nonzero_positions (((
      pert_infmatrix o hom_oper_infmatrix) o Rep_matrix) A)))"
      unfolding pert_matrix1_def hom_oper_matrix1_def by simp
    finally have nrows_fA: "nrows ((- ?f) A) =
      Suc (Max (fst ` nonzero_positions (((pert_infmatrix 
      o hom_oper_infmatrix) o Rep_matrix) A)))"
      by simp
    have Max_l: "(Max (fst ` nonzero_positions (Rep_matrix (?f A))))
      < Max (fst ` nonzero_positions (Rep_matrix A))"
    proof (rule Max_A_B)
      let ?n = "Max (fst ` nonzero_positions (Rep_matrix (?f A)))"
      show "∃m>?n .(m ∈ (fst ` nonzero_positions (Rep_matrix A)))"
      proof (rule exI [of _ "Suc ?n"], intro conjI, simp)
        have non_empty: "nonzero_positions (((pert_infmatrix 
          o hom_oper_infmatrix) o Rep_matrix) A) ≠ {}"
          using nonzero_positions_minus_f [of ?f A]
          using not_zero_impl_nonzero_pos [OF False] 
          unfolding pert_matrix1_def
          unfolding hom_oper_matrix1_def by simp
        have Max_in_set:
          "?n ∈ (fst ` nonzero_positions (((pert_infmatrix 
          o hom_oper_infmatrix) o Rep_matrix) A))"
          using Max_in [OF finite_imageI 
            [OF pert_infmatrix_hom_oper_infmatrix_finite, of "fst" A]]
          using non_empty
          unfolding image_def 
          unfolding pert_matrix1_def hom_oper_matrix1_def 
          by auto
        from Max_in_set obtain a 
          where A_a_n_0: "(pert_infmatrix (hom_oper_infmatrix (Rep_matrix A))) ?n a ≠ 0"
          unfolding nonzero_positions_def image_def by auto
        with pert_hom_oper_nonzero_incr_rows [of ?n a] 
        have "(Suc ?n, a - 1) ∈ nonzero_positions (Rep_matrix A)"
          unfolding nonzero_positions_def image_def by auto
        then show "Suc ?n ∈ fst ` nonzero_positions (Rep_matrix A)" 
          unfolding image_def [of fst] by force
      qed
      show "finite (fst ` nonzero_positions (Rep_matrix A))"
        using finite_imageI [OF finite_nonzero_positions [of A], of fst] .
      show "finite (fst ` nonzero_positions (Rep_matrix ((pert_matrix1 o hom_oper_matrix1) A)))"
        unfolding o_apply
        using finite_imageI by simp
    qed
    with nrows_A nrows_fA show ?thesis unfolding pert_matrix1_def hom_oper_matrix1_def by simp
  qed
qed

corollary nrows_alpha_decre:
  assumes A_not_null: "(A::'a::lordered_ring matrix) ≠ 0"
  shows "nrows (α A) < nrows A"
  using nrows_pert_hom_oper_decre [OF A_not_null]
  unfolding α_def
  unfolding pert_matrix_def
  unfolding hom_oper_matrix_def 
  unfolding fun_Compl_def
  unfolding o_apply .

subsection{*Matrices as an instance of differential 
  group satisfying the local nilpotency condition.*}

text{*With the previous results, we can finally prove that matrices are an instance 
  of differential group with a perturbation and a homotpy operator that additionally 
  satisfies the local nilpotency condition.*}

instantiation matrix :: (lordered_ring) diff_group_add_pert_hom_bound_exist
begin

lemma α_matrix_definition: "α = - (pert_matrix1 o hom_oper_matrix1)"
  unfolding α_def
  unfolding fun_Compl_def
  unfolding o_apply
  unfolding hom_oper_matrix_def
  unfolding pert_matrix_def ..

instance 
proof (default, rule allI)
  fix A :: "'a matrix"
  show "∃n::nat. ((α::'a matrix => 'a matrix)^ n) A = 0"
  proof (cases "A = 0")
    case True then show ?thesis using funpow.simps (1) [of α] using exI [of _ "0::nat"] by auto
  next
    case False
    obtain n where coeff_nilp: "nrows (((- (pert_matrix1 o hom_oper_matrix1))^n) A) = 0"
      using nrows_strict_decr [of "(- (pert_matrix1 o hom_oper_matrix1))"]
      using nrows_pert_hom_oper_decre
      using False by force
    show ?thesis
      unfolding α_matrix_definition
      using nrows_zero_impl_zero [OF coeff_nilp] ..
  qed
qed

end

text{*Unfortunately, the previous type @{text matrix} cannot be used with 
  the code generation facility. In order to get code generation, it is not 
  enoguh to prove the instances, but also the underlying types need to have 
  an executable counterpart.*}

text{*Thus, we used new definitions based on \emph{sparse matrices}, 
  which are based on lists and can be used 
  with the code generation facility.*}

text{*Unfortunately, sparse matrices cannot be proved to be an instance 
  of the previous type classes, since the results required are not equations 
  or definitional theorems, but more elaborated ones.*}

subsection {*Definition of the operations over sparse matrices.*}

fun transpose_spmat :: "('a::lordered_ring) spmat => 'a spmat"
  where
  transpose_spmat_Em: "transpose_spmat [] = []"
  | transpose_spmat_C_cons: "transpose_spmat ((i,v)#vs) = 
  (let m = map (λ (j, x). (j, [(i, x)])) v;
       M = transpose_spmat vs 
  in add_spmat (m, M))"

text{*Unfortunately,
  the following definition of @{term "differ_spmat"} does not preserve 
  the sortedness of the input matrix, 
  as it is shown in the lemmas below*}

fun differ_spmat :: "('a::lordered_ring) spmat => 'a spmat"
  where
  differ_spmat_Em: "differ_spmat [] = []"
  | differ_spmat_C_cons: "differ_spmat ((i,v)#vs) = 
  (let
  m = map (λ (j, x).  (if (j = 0) then (j, 0) 
                          else (if ((i + j) mod 2 = 0) 
                                 then (j - 1, (0::'a))
                                          else (j - 1, x)))) v;
  M = differ_spmat vs
  in add_spmat ([(i, m)], M))"

text{*Let's see what happens with @{term "differ_spmat"} which has degree $-1$ 
  and should not preserve the order of the colums*}
text{*In the first lemma it canbe seen that the predicate @{term "sorted_spvec"} 
  holds for @{term "differ_spmat"}. 
  This means that it keeps th sortedness of rows. This will be later proved.*}
text{*Unfortunately, @{term differ_spmat} doesnot preserve @{term "sorted_spmat"}, 
  which is the predicate checking that the elements in
  each column are sorted.*}

(*definition foo_def: "foo ==  (0::nat, (0::nat, 5::int) # (2, - 9) # (6, 8) # []) 
  # (1::nat, (1::nat, 4::int) # (2, 0) # (8, 8) # [])
  # (2::nat, (2::nat, 6::int) # (3, 7) # (9, 23) # [])
  # (29::nat, (3::nat, 5::int) # (4, 6) # (7, 8) # []) # []"

definition foo2_def: "foo2 = (3::nat, (2::nat, 4::int) # []) # []"

value "differ_spmat foo2"

lemma "sorted_spvec (differ_spmat foo) = True" 
  unfolding foo_def 
  unfolding differ_spmat.simps
  unfolding Let_def apply simp 
  unfolding sorted_spvec.simps by simp

lemma "sorted_spmat (differ_spmat (differ_spmat foo)) = False" 
  unfolding foo_def 
  unfolding differ_spmat.simps
  unfolding Let_def 
  apply simp 
  unfolding Let_def 
  apply simp unfolding sorted_spmat.simps unfolding sorted_spvec.simps by auto*)

text{*A lemma proving that @{term differ_spmat} is distributive 
  with respect to the appending of lists.*}

lemma differ_spmat_dist: "differ_spmat ((a, b) # A) = add_spmat (
              ([(a, (map (λ (j, x).  if (j = 0) then (j, (0::'a::lordered_ring)) 
                                     else (if ((a + j) mod 2 = 0) then (j - 1, (0::'a)) 
                                            else (j - 1, x))) b))]), differ_spmat A)"
  unfolding differ_spmat_C_cons unfolding Let_def ..

text{*The following definition @{term "differ_spmat'"} is equal to the one of 
  @{term "differ_spmat"} after the application of function @{term "sparse_row_matrix"}. 
  This means that both functions produce similar results over terms of @{term matrix} type.*}
text{*We used @{term differ_spmat'} for some induction methods since it produces always 
  elements of type @{typ "nat × ((nat × int) list)"}
  which somehow simplify induction proofs*}
text{*Later we will introduce a third operation @{term "differ_smat_sort"},
  which is also equivalent to these two
  in producing terms of @{term "matrix"} type and which additionally 
  preserves sortedness of vectors.*}

fun differ_spmat' :: "('a::lordered_ring) spmat => 'a spmat"
  where
  differ_spmat'_Em: "differ_spmat' [] = []"
  | differ_spmat'_C_cons: "differ_spmat' ((i,v)#vs) =
  (let
  m = map (λ (j, x).  if (j = 0) then (i, [(j, (0::'a))])
                                 else (if ((i + j) mod 2 = 0) then (i, [(j - 1, (0::'a))])
                                                else ((i, [(j - 1, x)])))) v;
  M = differ_spmat' vs
  in add_spmat (m, M))"

lemma differ_spmat'_dist: "differ_spmat' ((a, b) # A) = add_spmat (
               map (λ (j, x).  if (j = 0) then (a, [(j, (0::'a::lordered_ring))]) 
                                     else (if ((a + j) mod 2 = 0) then (a, [(j - 1, (0::'a))])
                                                  else  (a, [(j - 1, x)]))) b, differ_spmat' A)"
  unfolding differ_spmat'_C_cons unfolding Let_def by simp

text{*A similar situation arises with @{term "pert_spmat"} and @{term "pert_spmat'"}; 
  one is better in preserving sortedness and the other one is better for induction proofs.*}
text{*The following definition reiles on the fact that the result of evaluating 
  @{term "(0::nat) - 1"} over the natural numbers is equal to @{term "0::nat"}*}

fun pert_spmat :: "('a::lordered_ring) spmat => 'a spmat"
  where
  pert_spmat_Em: "pert_spmat [] = []"
  | pert_spmat_C_cons: "pert_spmat ((i,v)#vs) = 
  (let
  m = map (λ (j, x).  if (i = 0) then (j, (0::'a)) else
                                  if ((i + j) mod 2 = 0) then (j, (0::'a))
                                                              else (j, x)) v;
  M = pert_spmat vs
  in add_spmat ([(i - 1, m)], M))"

lemma pert_spmat_dist: "pert_spmat ((a, b) # A) = add_spmat (
  ([(a - 1, map (λ (j, x).  if (a = 0) then (j, (0::'a::lordered_ring)) else
                                       if ((a + j) mod 2 = 0) then (j, (0::'a))
                                                   else (j, x)) b)]) , pert_spmat A)"
  using pert_spmat_C_cons unfolding Let_def by simp

fun pert_spmat' :: "('a::lordered_ring) spmat => 'a spmat"
  where
  pert_spmat'_Em: "pert_spmat' [] = []"
  | pert_spmat'_C_cons: "pert_spmat' ((i,v)#vs) = 
  (let
  m = map (λ (j, x).  if (i = 0) then (i - 1, [(j, (0::'a))]) else
                                  if ((i + j) mod 2 = 0) then (i - 1, [(j, (0::'a))])
                                                              else  (i - 1, [(j, x)])) v;
  M = pert_spmat' vs
  in add_spmat (m, M))"

(*definition foo_def: "foo ==  (0::nat, (0::nat, 5::int) # (2, - 9) # (6, 8) # []) 
  # (1::nat, (1::nat, 4::int) # (2, 0) # (8, 8) # [])
  # (2::nat, (2::nat, 6::int) # (3, 7) # (9, 23) # [])
  # (29::nat, (3::nat, 5::int) # (4, 6) # (7, 8) # []) # []"

definition foo2_def: "foo2 = (0::nat, (7::nat, 4::int) # []) # []"

value "add_spmat ((pert_spmat foo), (minus_spmat (pert_spmat' foo)))"*)

lemma pert_spmat'_dist: "pert_spmat' ((a, b) # A) = add_spmat (
  (map (λ (j, x).  if (a = 0) then (a - 1, [(j, (0::'a::lordered_ring))]) else
                         if ((a + j) mod 2 = 0) then (a - 1, [(j, (0::'a))])
                                 else (a - 1, [(j, x)])) b) , pert_spmat' A)"
  using pert_spmat'_C_cons unfolding Let_def by simp

text{*In a similar way we provide two definitions for the @{term "hom_oper_spmat"} operation*}

fun hom_oper_spmat :: "('a::lordered_ring) spmat => 'a spmat"
  where
  hom_oper_spmat_Em: "hom_oper_spmat [] = []"
  | hom_oper_spmat_C_cons: "hom_oper_spmat ((i,v)#vs) = 
  (let
  m = map (λ (j, x). if ((i + j) mod (2::nat) = 1) then (j + 1, (0::'a))
                                                    else  (j + 1, x)) v;
  M = hom_oper_spmat vs
  in add_spmat ((i, m) # [], M))"

fun hom_oper_spmat' :: "('a::lordered_ring) spmat => 'a spmat"
  where
  hom_oper_spmat'_Em: "hom_oper_spmat' [] = []"
  | hom_oper_spmat'_C_cons: "hom_oper_spmat' ((i,v)#vs) = 
  (let
  m = map (λ (j, x). if ((i + j) mod (2::nat) = 1) then (i, [(j + 1, (0::'a))])
                                              else  (i, [(j + 1, x)])) v;
  M = hom_oper_spmat' vs
  in add_spmat (m, M))"

lemma hom_oper_spmat_dist: "hom_oper_spmat ((a, b) # A) = add_spmat (
  ([(a, map (λ(j, x). if (a + j) mod 2 = 1 then (j + 1, 0::'a::lordered_ring) 
                                           else (j + 1, x)) b)],
      hom_oper_spmat A))"
  unfolding hom_oper_spmat_C_cons unfolding Let_def by simp

lemma hom_oper_spmat'_dist: "hom_oper_spmat' ((a, b) # A) = add_spmat (
  map (λ (j, x).  if ((a + j) mod 2 = 1) then (a, [(j + 1, (0::'a::lordered_ring))])
                                  else (a, [(j + 1, x)])) b, hom_oper_spmat' A)"
  unfolding hom_oper_spmat'_C_cons unfolding Let_def by simp

subsection{*Some auxiliary lemmas*}

lemma map_impl_existence: 
  assumes map_f_l: "map f l = a # l'" 
  shows "∃a'. a' mem l ∧ f a' = a"
  using map_f_l by auto

lemma move_matrix_zero: shows "move_matrix 0 i j = 0"
  unfolding move_matrix_def
  unfolding zero_matrix_def 
  by (rule cong [of "Abs_matrix" "Abs_matrix"]) 
     (auto simp add: expand_fun_eq RepAbs_matrix [of "(λj i. 0::'a)"])

lemma sparse_row_matrix_cons2:
  "sparse_row_matrix [(i, (a::(nat×'a::lordered_ring)) # v)] 
    = singleton_matrix i (fst a) (snd a) + sparse_row_matrix [(i,v)]"
  unfolding sparse_row_matrix_cons
  unfolding sparse_row_matrix_empty
  apply simp
  unfolding move_matrix_add by simp

lemma sorted_spvec_trans: 
  assumes srtd: "sorted_spvec (a#b)" and c_in_b: "c mem b" 
  shows "fst a < fst c"
  using srtd c_in_b proof (induct b)
  case Nil
  {
    show ?case using Nil.prems (2) by auto
  }
next
  case Cons
  {
    fix aa list
    assume hypo: "[|sorted_spvec (a # list); c mem list|] ==> fst a < fst c"
      and prem1: "sorted_spvec (a # aa # list)" and prem2: "c mem aa # list"
    show "fst a < fst c"
    proof (cases "c mem list")
      case True
      {
        show ?thesis 
          using hypo [OF sorted_spvec_cons2 [OF prem1] True] .
      }
    next
      case False
      {
        have c_eq_aa: "c = aa"
          using False prem2 by (cases "c = aa") simp+
        show ?thesis
          using prem1 unfolding sym [OF c_eq_aa]
          unfolding sorted_spvec_step [of a "c#list"] 
          by simp
      }
    qed
  }
qed

subsection{*Properties of the operation @{text "hom_oper_spmat"}*}

text{*The operation @{term "hom_oper_spmat"} preserves sortedness 
  of vectors and matrices.*}
text{*It is also equivalent to the operation @{term "hom_matrix1"} 
  that we introduced previously, under the operation 
  @{term "sparse_row_matrix"}.*}

lemma add_spmat_el[simp]: "add_spmat (a, []) = a"
  by (cases a) auto

thm foldl_absorb0

text{*The following lemma is almost a copy of @{thm "foldl_absorb0"} except that it considers
  different types in the arguments*}

lemma foldl_absorb0':
  fixes l :: "nat × (nat × 'a::lordered_ring) list => 'a matrix"
  shows "!!M. foldl (λ(m::'a::lordered_ring matrix) x::nat × (nat × 'a) list. 
  m + (l x) ) M arr = 
   M + foldl (λm x. m + (l x)) (0::'a matrix) arr"
proof (induct arr)
  case Nil show ?case by simp
next
  case Cons
  fix a:: "nat × (nat × 'a) list"
  fix arr :: "(nat × (nat × 'a) list) list"
  fix M :: "'a matrix"
  assume hypo: "!!M. foldl (λm x. m + l x) M arr = M + foldl (λm x. m + l x) 0 arr"
  show " foldl (λm x. m + l x) M (a # arr) = M + foldl (λm x. m + l x) 0 (a # arr)"
    unfolding foldl.simps (2)
    unfolding hypo [of "M + l a"]
    unfolding hypo [of "0 + l a"] by simp
qed

lemma sparse_row_matrix_hom_oper_spvec: 
  shows "sparse_row_matrix (hom_oper_spmat ((i, v)#[])) 
  = sparse_row_matrix (hom_oper_spmat' ((i, v)# []))"
proof (induct v)
  case Nil show ?case
    unfolding sparse_row_matrix_def apply simp
    unfolding move_matrix_zero ..
next
  case Cons
  fix v::"(nat × 'a) list"
  fix a:: "nat × 'a"
  assume hypo: "sparse_row_matrix (hom_oper_spmat [(i, v)])
    = sparse_row_matrix (hom_oper_spmat' [(i, v)])"
  obtain p x where a_split: "a = (p, x)" by force
  show "sparse_row_matrix (hom_oper_spmat [(i, a#v)]) 
    = sparse_row_matrix (hom_oper_spmat' [(i, a#v)])"
    using hypo
    unfolding a_split
    unfolding sparse_row_matrix_def
    unfolding hom_oper_spmat.simps (2)
    unfolding hom_oper_spmat'.simps (2)
    unfolding Let_def
    unfolding hom_oper_spmat.simps (1)
    unfolding hom_oper_spmat'.simps (1)
    unfolding add_spmat.simps (2)
    apply (cases "(i + p) mod 2 = 1")
    apply auto
    unfolding move_matrix_zero
    apply (rule cong [of "foldl
      (λm r. m + move_matrix (sparse_row_vector (snd r)) (int (fst r)) (0::int)) 0"])
    apply fast
    apply simp
    unfolding move_matrix_add
    unfolding move_matrix_singleton
    apply simp
    unfolding foldl_absorb0' [of _ "singleton_matrix i (nat ((1::int) + int p)) x"] ..
qed

text{*The following operation proves that the two operations @{term "hom_oper_spmat"} 
  and @{term "hom_oper_spmat'"} are equivalent under 
  the application of @{term "sparse_row_matrix"}.*}

lemma sparse_row_matrix_hom_oper_spmat:
  "sparse_row_matrix (hom_oper_spmat A)
  = sparse_row_matrix (hom_oper_spmat' A)"
proof (induct A)
  case Nil
  {
    show ?case using hom_oper_spmat.simps (1) hom_oper_spmat'.simps (1) by simp
  }
next
  case Cons
  {
    fix a :: "nat × (nat × 'a) list"
    obtain i v where a_split: "a = (i, v)" by force
    fix  A:: "'a spmat"
    assume hypo: "sparse_row_matrix (hom_oper_spmat A)
      = sparse_row_matrix (hom_oper_spmat' A)"
    show "sparse_row_matrix (hom_oper_spmat (a # A)) 
      = sparse_row_matrix (hom_oper_spmat' (a # A))"
      unfolding a_split
      using sparse_row_matrix_hom_oper_spvec [of i v]
      unfolding hom_oper_spmat_dist
      unfolding hom_oper_spmat'_dist
      unfolding sparse_row_add_spmat
      using hypo by simp
  }
qed
      
text{*The following lemma proves that @{term hom_oper_spmat} 
  preserves the sortedness of matrices w.r.t @{term hom_oper_spvec}.*}

lemma sorted_spvec_hom_oper: 
  assumes srtd_A: "sorted_spvec A" 
  shows "sorted_spvec (hom_oper_spmat A)"
  using srtd_A proof (induct A)
  case Nil
  {
    show ?case by simp
  }
next
  case Cons
  {
    fix a :: "nat × (nat × 'a) list"
    obtain i v where a_split: "a = (i, v)" by force
    fix A :: "'a spmat"
    assume hypo: "sorted_spvec A ==> sorted_spvec (hom_oper_spmat A)" 
      and prem: "sorted_spvec (a # A)"
    have sorted_spvec_hos: "sorted_spvec (hom_oper_spmat A)"
      using hypo [OF sorted_spvec_cons1 [OF prem]] .
    show "sorted_spvec (hom_oper_spmat (a # A))"
      unfolding a_split
      unfolding hom_oper_spmat.simps
      unfolding Let_def
      by (rule sorted_spvec_add_spmat [OF _ sorted_spvec_hos], 
          induct v, 
          simp_all add: sorted_spvec.simps)
  }
qed

lemma sorted_spvec_singleton[simp]: "sorted_spvec [(i, v)]"
  using sorted_spvec.simps (2) [of "(i, v)" "[]"] by simp

lemma sorted_spvec_hom_oper_vec: assumes ss_v: "sorted_spvec v" 
  shows "sorted_spvec (map (λ(j, x). if (i + j) mod 2 = Suc 0 
  then (j + 1, 0::'a::lordered_ring) else (j + 1, x)) v)"
  (is "sorted_spvec (map ?f v)")
  using ss_v proof (induct v)
  case Nil show ?case by simp
next
  case Cons
  fix a :: "(nat × 'a)"
  obtain i va where a_split: "a = (i, va)" by force
  fix v :: "(nat × 'a) list"
  assume hypo: "sorted_spvec v ==> sorted_spvec (map ?f v)"
    and prem: "sorted_spvec (a # v)"
  have ss_v': "sorted_spvec v" 
    using prem unfolding sorted_spvec.simps (2) by (cases v) auto
  have ss_fv': "sorted_spvec (map ?f v)" using hypo [OF ss_v'] .
  show "sorted_spvec (map ?f (a # v))"
  proof (unfold map.simps)
    show "sorted_spvec ((?f a) # (map ?f v))"
    proof (cases "map ?f v")
      case Nil
      {
        show ?thesis
          unfolding Nil
          unfolding a_split
          unfolding split_conv by simp
      }
    next
      case Cons
      {
        fix c :: "nat × 'a"
        fix lista :: "(nat × 'a) list"
        assume hypo_cons: "map ?f v = c # lista"
        show "sorted_spvec ((?f a) # (map ?f v))"
        proof -
          have "sorted_spvec (c # lista)" 
            using ss_fv'
            unfolding hypo_cons .
          moreover have " fst (?f a) < fst c"
          proof -
            obtain c' where c'_def: "(c' mem v) ∧ (?f c' = c)"
              using hypo_cons using map_impl_existence [of ?f v c lista] by auto
            with prem have fstb_l_fstc': "fst a < fst c'" 
              using sorted_spvec_trans [of a v c'] by simp
            with c'_def show ?thesis proof auto
              have lhs: "fst (?f (fst a, snd a)) = (fst a) + 1" by force
              have rhs: "fst (?f (fst c', snd c')) = (fst c') + 1" by force
              from lhs rhs and fstb_l_fstc' show "fst (?f a) < fst (?f c')" by simp
            qed
          qed
          ultimately show ?thesis
            unfolding hypo_cons
            unfolding sorted_spvec.simps (2) by simp
        qed
      }
    qed
  qed
qed

lemma sorted_spmat_hom_oper:
  assumes sorted_spmat_A: "sorted_spmat A" 
  shows "sorted_spmat (hom_oper_spmat A)"
  using sorted_spmat_A proof (induct A)
  case Nil
  {
    show ?case by simp
  }
next
  case Cons
  {
    fix a :: "nat × (nat × 'a) list"
    obtain i v where a_split: "a = (i, v)" by force
    fix A :: "'a spmat"
    assume hypo: "sorted_spmat A ==> sorted_spmat (hom_oper_spmat A)"
      and prem: "sorted_spmat (a # A)"
    have sspm: "sorted_spmat (hom_oper_spmat A)"
      using sorted_spmat.simps (2) [of a A] using prem using hypo by fast
    have stsp: "sorted_spvec v" using prem unfolding a_split
      unfolding sorted_spmat.simps (2) unfolding snd_conv ..
    show "sorted_spmat (hom_oper_spmat (a # A))"
      unfolding a_split
      unfolding hom_oper_spmat_dist
      apply (rule sorted_spmat_add_spmat [OF _ sspm])
      apply simp
      using sorted_spvec_hom_oper_vec [OF stsp, of i] .
  }
qed

subsection{*Properties of the function @{term "differ_spmat"}*}

lemma sparse_row_matrix_differ_spvec: 
  "sparse_row_matrix (differ_spmat ((i, v)#[])) 
  = sparse_row_matrix (differ_spmat' ((i, v)# []))"
proof (induct v)
  case Nil show ?case
    unfolding sparse_row_matrix_def apply simp
    unfolding move_matrix_zero ..
next
  case Cons
  fix v::"(nat × 'a) list"
  fix a:: "nat × 'a"
  assume hypo: "sparse_row_matrix (differ_spmat [(i, v)])
    = sparse_row_matrix (differ_spmat' [(i, v)])"
  obtain p x where a_split: "a = (p, x)" by force
  show "sparse_row_matrix (differ_spmat [(i, a#v)]) 
    = sparse_row_matrix (differ_spmat' [(i, a#v)])"
    using hypo
    unfolding a_split
    unfolding sparse_row_matrix_def
    unfolding differ_spmat.simps (2)
    unfolding differ_spmat'.simps (2)
    unfolding Let_def
    unfolding differ_spmat.simps (1)
    unfolding hom_oper_spmat'.simps (1)
    unfolding add_spmat.simps (2)
    apply (cases "p = 0")
    apply auto
    unfolding move_matrix_zero apply simp 
    apply (cases "(i + p) mod 2 = 0")
    apply auto
    unfolding move_matrix_add
    unfolding move_matrix_singleton
    apply simp
    unfolding foldl_absorb0' [of _ "singleton_matrix i (p - Suc 0) x"] ..
qed
  
lemma sparse_row_matrix_differ_spmat: 
  "sparse_row_matrix (differ_spmat A) 
  = sparse_row_matrix (differ_spmat' A)"
proof (induct A)
  case Nil
  {
    show ?case using differ_spmat.simps (1) differ_spmat'.simps (1) by simp
  }
next
  case Cons
  {
    fix a :: "nat × (nat × 'a) list"
    obtain i v where a_split: "a = (i, v)" by force
    fix  A:: "'a spmat"
    assume hypo: "sparse_row_matrix (differ_spmat A)
      = sparse_row_matrix (differ_spmat' A)"
    show "sparse_row_matrix (differ_spmat (a # A)) 
      = sparse_row_matrix (differ_spmat' (a # A))"
      unfolding a_split
      using sparse_row_matrix_differ_spvec [of i v]
      unfolding differ_spmat_dist
      unfolding differ_spmat'_dist
      unfolding sparse_row_add_spmat
      using hypo by simp
  }
qed

text{*This lemma might be omitted. 
  @{term "differ_spmat"} preserves the sortedness of the rows but not the one 
  of vectors, so this is why we will later introduce @{term "differ_spmat_sort"}*}

lemma sorted_spvec_differ: 
  assumes srtd_A: "sorted_spvec A" 
  shows "sorted_spvec (differ_spmat A)"
  using srtd_A proof (induct A)
  case Nil
  {
    show ?case by simp
  }
next
  case Cons
  {
    fix a :: "nat × (nat × 'a) list"
    obtain i v where a_split: "a = (i, v)" by force
    fix A :: "'a spmat"
    assume hypo: "sorted_spvec A ==> sorted_spvec (differ_spmat A)" 
      and prem: "sorted_spvec (a # A)"
    have ss_d: "sorted_spvec (differ_spmat A)"
      using prem unfolding sorted_spvec.simps (2) 
      using hypo by (cases A) auto
    show "sorted_spvec (differ_spmat (a # A))"
      unfolding a_split
      unfolding differ_spmat.simps (2)
      unfolding Let_def
      apply (rule sorted_spvec_add_spmat)
      using sorted_spvec_singleton [of i "map (λ(j, x).
        if j = 0 then (j, 0::'a) else if (i + j) mod 2 = 0 
        then (j - 1, 0::'a) else (j - 1, x)) v"]
      using ss_d .
  }
qed

subsection{*Some ideas to keep sorted vectors inside a matrix*}

text{*We will define two functions to keep vectors sorted. 
  The first one, @{text insert_sorted} inserts a pair 
  in a list in its right position.*}

text{*From the definition it can be seen that it does not 
  pay attention to whether the list was originally sorted or not.*}

primrec insert_sorted:: "(nat × 'a::lordered_ring) => 'a spvec => 'a spvec"
where
  insert_sorted_nil: "insert_sorted a [] = [a]"
  | insert_sorted_cons: "insert_sorted a (b # c) = 
                    (if (fst a < fst b) then (a # b # c) else 
                        (if fst a = fst b then (fst a, snd a + snd b) # c 
                              else b # (insert_sorted a c)))"

lemma [simp]: "sorted_spvec [a]" 
  unfolding sorted_spvec.simps (2) [of a "[]"] by simp

text{*The following lemma proves that the @{term insert_sorted} 
  preserves sortedness of vectors.*}

lemma insert_preserves_sorted: 
  assumes sorted_spvec_v: "sorted_spvec v" 
  shows "sorted_spvec (insert_sorted a v)"
  using sorted_spvec_v proof (induct v)
  case Nil
  {
    show ?case unfolding insert_sorted_nil unfolding sorted_spvec.simps by simp
  }
next
  case Cons
  {
    fix u :: "nat × 'a"
    fix w :: "(nat × 'a) list"
    assume hypo: "sorted_spvec w ==> sorted_spvec (insert_sorted a w)" 
      and prem: "sorted_spvec (u # w)"
    have ss_w: "sorted_spvec w"
      using sorted_spvec_cons1 [OF prem] .
    have ssi_a_w: "sorted_spvec (insert_sorted a w)"
      using hypo [OF ss_w].
    show "sorted_spvec (insert_sorted a (u # w))"
    proof (cases "fst a < fst u")
      case True
      {
        show ?thesis 
          unfolding insert_sorted.simps (2)
          using True
          using if_True
          apply simp
          unfolding sorted_spvec.simps (2) [of a "u # w"] 
          using prem by auto
      }
    next
      case False note nl_a_u = False
      {
        show ?thesis
        proof (cases "fst a = fst u")
          case True
          {
            show ?thesis 
              unfolding insert_sorted.simps
              using nl_a_u 
              using True apply simp using prem unfolding sorted_spvec.simps
              by (cases w) auto
          }
        next
          case False note ne_a_u = False
          {
            have sorted: "sorted_spvec (u # (insert_sorted a w))"
            proof (cases w)
              case Nil
              {
                show ?thesis 
                  unfolding Nil 
                  unfolding insert_sorted.simps (1) 
                  unfolding sorted_spvec.simps (2) [of u "[a]"] 
                  using nl_a_u False by simp
              }
            next
              case Cons
              {
                fix wa wlist
                assume hypow: "w = wa # wlist" 
                show "sorted_spvec (u # insert_sorted a w)"
                proof (cases "fst a < fst wa")
                  case True
                  {
                    have helper: "insert_sorted a w = a # w" 
                      using True
                      unfolding hypow
                      unfolding insert_sorted.simps (2) [of a wa wlist] 
                      by simp
                    show ?thesis 
                      unfolding helper 
                      unfolding sorted_spvec.simps (2) [of u "a # w"] 
                      unfolding hypow
                      apply simp
                      unfolding sorted_spvec.simps (2) [of a "wa # wlist"]
                      using hypow using ss_w True
                      using nl_a_u ne_a_u by simp
                  }
                next
                  case False note a_eq_wa = False
                  {
                    show ?thesis
                    proof (cases "fst a = fst wa")
                      case True
                      {
                        have helper: "insert_sorted a w = (fst a, snd a + snd wa) # wlist"
                          using True and a_eq_wa
                          unfolding hypow 
                          unfolding insert_sorted.simps (2) by simp
                        show ?thesis
                          unfolding helper
                          unfolding sorted_spvec.simps (2) [of u "(fst a, snd a + snd wa) # wlist"] 
                          using ss_w using nl_a_u ne_a_u
                          using hypow
                          apply simp
                          unfolding True unfolding sorted_spvec.simps
                          by (cases wlist) auto
                      }
                    next
                      case False
                      {
                        have helper: "insert_sorted a w = wa # insert_sorted a wlist"
                          using False and a_eq_wa
                          unfolding hypow
                          unfolding insert_sorted.simps (2) by simp
                        show ?thesis 
                          unfolding helper
                          using ssi_a_w
                          unfolding hypow
                          unfolding insert_sorted.simps (2) [of a ]
                          using False a_eq_wa
                          apply simp
                          using sorted_spvec_cons1 [of wa "insert_sorted a wlist"]
                          unfolding sorted_spvec.simps (2) [of u "wa # insert_sorted a wlist"]
                          apply simp using prem
                          unfolding hypow unfolding sorted_spvec.simps (2) [of u "wa # wlist"] 
                          by simp
                      }
                    qed
                  }
                qed
              }
            qed
          }
          then show ?thesis
            using nl_a_u False
            unfolding insert_sorted.simps (2) by simp
        qed
      }
    qed
  }
qed

text{*The following operation, @{term "sort_spvec"}, 
  takes the first element of a list and introduces it in its corresponding position,
  by means of the @{term "insert_sort"} operation.*}
text{*Applying this process recursively, we get to sort the vector.*}

primrec sort_spvec :: "'a::lordered_ring spvec => 'a spvec"
  where
  sort_spvec_nil: "sort_spvec [] = []"
  | sort_spvec_cons: "sort_spvec (a # b) = (insert_sorted a (sort_spvec b))"

lemma sort_is_sorted: shows "sorted_spvec (sort_spvec v)"
proof (induct v)
  case Nil
  {
    show ?case by simp
  }
next
  case Cons
  {
    fix a w
    assume hypo: "sorted_spvec (sort_spvec w)"
    show "sorted_spvec (sort_spvec (a # w))" 
      unfolding sort_spvec.simps (2)
      using insert_preserves_sorted [OF hypo, of a] .
  }
qed

text{*The operation of sorting is compatible with the addition of sparse vectors*}

lemma insert_pair_add_pair:
  shows "insert_sorted (a, b) v = add_spvec ([(a, b)], v)"
proof (induct v)
  case Nil show ?case by simp
next
  case Cons
  {
    fix i :: "nat × 'a"
    obtain j c where i_split: "i = (j, c)" by force
    fix v :: "(nat × 'a) list"
    assume hypo: "insert_sorted (a, b) v = add_spvec ([(a, b)], v)"
    show "insert_sorted (a, b) (i # v) = add_spvec ([(a, b)], i # v)"
      unfolding i_split
    proof (cases "a < j")
      case True
      {
        show "insert_sorted (a, b) ((j, c) # v) = add_spvec ([(a, b)], (j, c) # v)"
          using True by simp
      }
    next
      case False note a_nl_aa = False
      {
        show "insert_sorted (a, b) ((j, c) # v) = add_spvec ([(a, b)], (j, c) # v)"
        proof (cases "a = j")
          case True
          {
            show ?thesis using True a_nl_aa by simp
          }
        next
          case False
          {
            show ?thesis using False a_nl_aa apply simp unfolding hypo ..
          }
        qed
      }
    qed
  }
qed

text{*The following operation is similar to @{term differ_spmat} 
  except that it applies @{term sort_spvec} to every vector.*}

fun differ_spmat_sort :: "('a::lordered_ring) spmat => 'a spmat"
  where
  differ_spmat_sort_Em: "differ_spmat_sort [] = []"
  | differ_spmat_sort_C_cons: "differ_spmat_sort ((i,v)#vs) = 
  (let
  m = sort_spvec (map (λ (j, x).  if j = 0 then (j, 0)
                             else if (i + j) mod 2 = 0 then (j - 1, 0) else (j - 1, x)) v);
  M = differ_spmat_sort vs
  in add_spmat ([(i, m)], M))"

lemma differ_spmat_sort_dist:
  shows "differ_spmat_sort ((i, v) # vs) = add_spmat (
             ([(i, sort_spvec (
                      map (λ (j, x::'a::lordered_ring). if j = 0 then (j, 0)
                         else if (i + j) mod 2 = 0 then (j - 1, 0) 
                             else (j - 1, x)) v))]), differ_spmat_sort vs)"
  unfolding differ_spmat_sort_C_cons unfolding Let_def ..

lemma sorted_spvec_differ_spmat_sort: 
  assumes srtd_A: "sorted_spvec A" 
  shows "sorted_spvec (differ_spmat_sort A)"
  using srtd_A proof (induct A)
  case Nil
  {
    show ?case by simp
  }
next
  case Cons
  {
    fix a :: "nat × (nat × 'a) list"
    obtain i v where a_split: "a = (i, v)" by force
    fix A :: "'a spmat"
    assume hypo: "sorted_spvec A ==> sorted_spvec (differ_spmat_sort A)"
      and prem: "sorted_spvec (a # A)"
    show "sorted_spvec (differ_spmat_sort (a # A))"
      unfolding a_split
      unfolding differ_spmat_sort.simps (2) [of i v A]
      unfolding Let_def
      apply (rule sorted_spvec_add_spmat)
      using sorted_spvec_singleton [of i _] apply simp
      using prem hypo
      unfolding sorted_spvec.simps (2)
      apply (cases A) by auto
  }
qed

text{*As it may be seen in the following lemma, as far as we now have
  the operation @{term sort_spvec} sorting every vector,
  the lemma does not require the premise of @{term A} being previously sorted.*}

lemma sorted_spmat_differ_spmat_sort:
  shows "sorted_spmat (differ_spmat_sort A)"
proof (induct A)
  case Nil
  {
    show ?case by simp
  }
next
  case Cons
  {
    fix a :: "nat × (nat × 'a) list"
    obtain i v where a_split: "a = (i, v)" by force
    fix A :: "'a spmat"
    assume hypo: "sorted_spmat (differ_spmat_sort A)"
    show "sorted_spmat (differ_spmat_sort (a # A))"
      unfolding a_split
      unfolding differ_spmat_sort_dist
      apply (rule sorted_spmat_add_spmat [OF _ _])
      unfolding sorted_spmat.simps (2)
      unfolding sorted_spmat.simps (1)
      unfolding snd_conv apply simp
      using sort_is_sorted using hypo .
  }
qed

primrec sort_spvec_spmat :: "'a::lordered_ring spmat => 'a spmat"
  where
  sort_spvec_spmat_nil: "sort_spvec_spmat [] = []"
  | sort_spvec_spmat_cons: "sort_spvec_spmat (a # as) 
  = (fst a, sort_spvec (snd a)) # (sort_spvec_spmat as)"

lemma sort_spvec_spmat_impl_sorted_spmat: 
  shows "sorted_spmat (sort_spvec_spmat A)"
proof (induct A)
  case Nil show ?case by simp
next
  case Cons 
  fix a A
  assume hypo: "sorted_spmat (sort_spvec_spmat A)"
  show "sorted_spmat (sort_spvec_spmat (a # A))"
    unfolding sort_spvec_spmat.simps (2)
    unfolding sorted_spmat.simps (2)
    apply (rule conjI)
    unfolding snd_conv
    using sort_is_sorted [of "snd a"] using hypo .
qed

text{*The following lemma shows that even in the cases 
  where @{term "differ_spmat"} produces non sorted matrices, 
  they are equal to the ones obtained with @{term "differ_spmat_sort"} 
  under the application @{term "sparse_row_matrix"}*}

text{*We illustrate this fact with an example with @{term foo}.*}

(*definition foo_def: "foo ==  (0::nat, (0::nat, 5::int) # (2, - 9) # (6, 8) # []) 
  # (1::nat, (1::nat, 4::int) # (2, 0) # (8, 8) # [])
  # (2::nat, (2::nat, 6::int) # (3, 7) # (9, 23) # [])
  # (29::nat, (3::nat, 5::int) # (4, 6) # (7, 8) # []) # []"

lemma "sparse_row_matrix (differ_spmat (differ_spmat foo))
  = sparse_row_matrix (differ_spmat_sort (differ_spmat_sort foo))"
  unfolding foo_def
  apply (simp add: differ_spmat.simps differ_spmat_sort.simps Let_def)
  unfolding sparse_row_matrix_def by simp
*)

lemma sparse_row_vector_not_sorted: 
  shows "sparse_row_vector (a # b # []) = sparse_row_vector (b # a # [])"
  unfolding sparse_row_vector_cons by simp

lemma sparse_row_vector_dist:
  shows "sparse_row_vector ([(i, j + k)]) = sparse_row_vector ((i, j) # [(i, k)])"
  unfolding sparse_row_vector_cons apply simp
  unfolding singleton_matrix_def
  unfolding plus_matrix_def
  unfolding combine_matrix_def
  apply (rule comb [of "Abs_matrix" "Abs_matrix"], simp)
  unfolding combine_infmatrix_def
  using RepAbs_matrix [of "(λm n. if 0 = m ∧ i = n then j else (0::'a))"] 
  using exI [of "(λm. ∀ja ia. m ≤ ja --> (if 0 = ja ∧ i = ia 
    then j else (0::'a)) = (0::'a))" "Suc 0"]
  using exI [of  "(λn. ∀ja ia. n ≤ ia --> (if (0::nat) = ja ∧ i = ia 
    then j else (0::'a)) = (0::'a))" "Suc i"]
  apply auto
  using RepAbs_matrix [of "(λm n. if 0 = m ∧ i = n then k else (0::'a))"]
  using exI [of "(λm. ∀ja ia. m ≤ ja --> 
    (if 0 = ja ∧ i = ia then k else (0::'a)) = (0::'a))" "Suc 0"]
  using exI [of  "(λn. ∀ja ia. n ≤ ia --> 
    (if (0::nat) = ja ∧ i = ia then k else (0::'a)) = (0::'a))" "Suc i"]
  apply auto
  unfolding expand_fun_eq by simp

lemma insert_sorted_preserves_sparse_row_vector:
  shows "sparse_row_vector (a # v) = sparse_row_vector (insert_sorted a v)"
proof (induct v)
  case Nil
  {
    show ?case by simp
  }
next
  case Cons
  {
    fix b :: "nat × 'a"
    fix v :: "(nat × 'a) list"
    assume hypo: "sparse_row_vector (a # v)
      = sparse_row_vector (insert_sorted a v)"
    show "sparse_row_vector (a # b # v) 
      = sparse_row_vector (insert_sorted a (b # v))"
    proof (cases "fst a < fst b")
      case True
      {
        show ?thesis 
          unfolding insert_sorted.simps using True by simp
      }
    next
      case False note a_nl_b = False
      {
        show ?thesis
        proof (cases "fst a = fst b")
          case True
          {
            show ?thesis 
              unfolding insert_sorted.simps using a_nl_b using True
              apply simp
              using sparse_row_vector_dist [of "fst b" "snd a" "snd b"]
              unfolding sparse_row_vector_cons by simp
          }
        next
          case False
          {
            show ?thesis 
              unfolding insert_sorted.simps 
              using a_nl_b False apply simp 
              unfolding sym [OF sparse_row_vector_cons] 
              using hypo by simp
          }
        qed
      }
    qed
  }
qed

lemma sort_spvec_eq_sparse_row_vector: 
  shows "sparse_row_vector v = sparse_row_vector (sort_spvec v)"
proof (induct v)
  case Nil
  {
    show ?case by simp
  }
next
  case Cons
  {
    fix a
    fix v
    assume hypo: "sparse_row_vector v = sparse_row_vector (sort_spvec v)"
    show "sparse_row_vector (a # v) = sparse_row_vector (sort_spvec (a # v))"
      unfolding sort_spvec.simps
      unfolding sym [OF insert_sorted_preserves_sparse_row_vector]
      unfolding sparse_row_vector_cons 
      unfolding hypo ..
  }
qed

lemma sort_spvec_eq_sparse_row_matrix:
  shows "sparse_row_matrix [(i, v)]
  = sparse_row_matrix [(i, sort_spvec v)]"
  unfolding sparse_row_matrix_def
  using sort_spvec_eq_sparse_row_vector [of v] by simp

lemma sparse_row_matrix_differ_spmat_differ_spmat_sort: 
  shows "sparse_row_matrix (differ_spmat A) 
  = sparse_row_matrix (differ_spmat_sort A)"
proof (induct A)
  case Nil
  {
    show ?case by simp
  }
next
  case Cons
  {
    fix a :: "nat × (nat × 'a) list"
    obtain i v where a_split: "a = (i, v)" by force
    fix A :: "'a spmat"
    assume hypo: "sparse_row_matrix (differ_spmat A)
      = sparse_row_matrix (differ_spmat_sort A)"
    show "sparse_row_matrix (differ_spmat (a # A)) 
      = sparse_row_matrix (differ_spmat_sort (a # A))"
      unfolding a_split
      unfolding differ_spmat_dist
      unfolding differ_spmat_sort_dist
      unfolding sparse_row_add_spmat
      unfolding hypo
      using sort_spvec_eq_sparse_row_matrix by auto
  }
qed

subsection{*Properties of the perturbation operator*}

lemma sparse_row_matrix_pert_spvec:
  shows "sparse_row_matrix (pert_spmat ((i, v)#[])) 
  = sparse_row_matrix (pert_spmat' ((i, v)# []))"
proof (induct v)
  case Nil show ?case
    unfolding sparse_row_matrix_def apply simp
    unfolding move_matrix_zero ..
next
  case Cons
  fix v::"(nat × 'a) list"
  fix a:: "nat × 'a"
  assume hypo: "sparse_row_matrix (pert_spmat [(i, v)])
    = sparse_row_matrix (pert_spmat' [(i, v)])"
  obtain p x where a_split: "a = (p, x)" by force
  show "sparse_row_matrix (pert_spmat [(i, a#v)]) 
    = sparse_row_matrix (pert_spmat' [(i, a#v)])"
    using hypo
    unfolding a_split
    unfolding sparse_row_matrix_def
    unfolding pert_spmat.simps (2)
    unfolding pert_spmat'.simps (2)
    unfolding Let_def
    unfolding pert_spmat.simps (1)
    unfolding hom_oper_spmat'.simps (1)
    unfolding add_spmat.simps (2)
    apply (cases "i = 0")
    apply auto
    apply (cases "(i + p) mod 2 = 0")
    apply auto
    unfolding move_matrix_zero
    apply (rule cong [of "foldl
      (λm r. m + move_matrix (sparse_row_vector (snd r)) (int (fst r)) (0::int)) 0"])
    apply fast
    apply simp
    unfolding move_matrix_add
    unfolding move_matrix_singleton
    apply simp
    unfolding foldl_absorb0' [of _ "singleton_matrix (i - Suc 0) p x"] ..
qed

lemma sparse_row_matrix_pert_spmat: 
  shows "sparse_row_matrix (pert_spmat A) = sparse_row_matrix (pert_spmat' A)"
proof (induct A)
  case Nil
  {
    show ?case by simp
  }
next
  case Cons
  {
    fix a :: "nat × (nat × 'a) list"
    obtain i v where a_split: "a = (i, v)" by force
    fix A :: "'a spmat"
    assume hypo: "sparse_row_matrix (pert_spmat A)
      = sparse_row_matrix (pert_spmat' A)"
    show "sparse_row_matrix (pert_spmat (a # A))
      = sparse_row_matrix (pert_spmat' (a # A))"
      unfolding a_split
      unfolding pert_spmat_dist
      unfolding pert_spmat'_dist
      unfolding sparse_row_add_spmat
      unfolding hypo
      using sparse_row_matrix_pert_spvec [of i v]
      unfolding pert_spmat.simps
      unfolding pert_spmat'.simps
      unfolding Let_def by simp
  }
qed

text{*The following oepration is equivalent to 
  @{term pert_spmat} except that it sorts every vector.*}

fun pert_spmat_sort :: "('a::lordered_ring) spmat => 'a spmat"
  where
  pert_spmat_sort_Em: "pert_spmat_sort [] = []"
  | pert_spmat_sort_C_cons: "pert_spmat_sort ((i,v)#vs) = 
  (let
  m = sort_spvec (map (λ (j, x). if i = 0 then (j, 0) else 
                      if (i + j) mod 2 = 0 then (j, 0) else (j, x)) v);
  M = pert_spmat_sort vs
  in add_spmat ([(i - 1, m)], M))"

lemma pert_spmat_sort_dist: "pert_spmat_sort ((i, v) # vs) = add_spmat (
        ([(i - 1, sort_spvec (map (λ (j, x). if i = 0 then (j, 0) else
                  if (i + j) mod 2 = 0 then (j, 0) else (j, x)) v))]), pert_spmat_sort vs)"
  using pert_spmat_sort_C_cons [of i v vs] unfolding Let_def .

text{*Once again, the premise of @{term A} being sorted can 
  be avoided in the following lemma since @{term pert_spmat_sort} 
  will do the job*}

lemma sorted_spmat_pert_spmat_sort: 
  shows "sorted_spmat (pert_spmat_sort A)"
proof (induct A)
  case Nil
  {
    show ?case by simp
  }
next
  case Cons
  {
    fix a :: "nat × (nat × 'a) list"
    obtain i v where a_split: "a = (i, v)" by force
    fix A :: "'a spmat"
    assume hypo: "sorted_spmat (pert_spmat_sort A)"
    show "sorted_spmat (pert_spmat_sort (a # A))"
      unfolding a_split
      unfolding pert_spmat_sort_dist
      apply (rule sorted_spmat_add_spmat [OF _ _])
      unfolding sorted_spmat.simps (2)
      unfolding sorted_spmat.simps (1)
      unfolding snd_conv apply simp
      using sort_is_sorted using hypo .
  }
qed

text{*The following lemma proves that both @{term pert_spmat} and 
  @{text pert_spmat_sort} produce the same result 
  under the application of @{term sparse_row_matrix}, or, 
  in other words, as terms of @{term matrix} type.*}

lemma sparse_row_matrix_pert_spmat_eq_pert_spmat_sort: 
  shows "sparse_row_matrix (pert_spmat A) = sparse_row_matrix (pert_spmat_sort A)"
proof (induct A)
  case Nil
  {
    show ?case by simp
  }
next
  case Cons
  {
    fix a :: "nat × (nat × 'a) list"
    obtain i v where a_split: "a = (i, v)" by force
    fix A :: "'a spmat"
    assume hypo: "sparse_row_matrix (pert_spmat A)
      = sparse_row_matrix (pert_spmat_sort A)"
    show "sparse_row_matrix (pert_spmat (a # A)) 
      = sparse_row_matrix (pert_spmat_sort (a # A))"
      unfolding a_split
      unfolding pert_spmat_dist
      unfolding pert_spmat_sort_dist
      unfolding sparse_row_add_spmat
      unfolding hypo
      using sort_spvec_eq_sparse_row_matrix by auto
  }
qed

lemma sorted_spvec_pert_spmat_sort: 
  assumes srtd_A: "sorted_spvec A" 
  shows "sorted_spvec (pert_spmat_sort A)"
  using srtd_A proof (induct A)
  case Nil
  {
    show ?case by simp
  }
next
  case Cons
  {
    fix a :: "nat × (nat × 'a) list"
    obtain i v where a_split: "a = (i, v)" by force
    fix A :: "'a spmat"
    assume hypo: "sorted_spvec A ==> sorted_spvec (pert_spmat_sort A)"
      and prem: "sorted_spvec (a # A)"
    show "sorted_spvec (pert_spmat_sort (a # A))"
      unfolding a_split
      unfolding pert_spmat_sort.simps (2) [of i v A]
      unfolding Let_def
      apply (rule sorted_spvec_add_spmat)
      using sorted_spvec_singleton [of i _] apply simp
      using prem hypo
      unfolding sorted_spvec.simps (2)
      apply (cases A) by auto
  }
qed

text{*The followig lemma may be omitted since we do not 
  care about the properties of @{term pert_spmat}, but of the ones from
  @{term pert_spmat_sort}*}

lemma sorted_spvec_pert: 
  assumes srtd_A: "sorted_spvec A" 
  shows "sorted_spvec (pert_spmat A)"
  using srtd_A proof (induct A)
  case Nil
  {
    show ?case by simp
  }
next
  case Cons
  {
    fix a :: "nat × (nat × 'a) list"
    obtain i v where a_split: "a = (i, v)" by force
    fix A :: "'a spmat"
    assume hypo: "sorted_spvec A ==> sorted_spvec (pert_spmat A)" 
      and prem: "sorted_spvec (a # A)"
    have sorted_spvec_ps: "sorted_spvec (pert_spmat A)"
      using hypo [OF sorted_spvec_cons1 [OF prem]] .
    show "sorted_spvec (pert_spmat (a # A))"
      unfolding a_split
      unfolding pert_spmat.simps
      unfolding Let_def
      by (rule sorted_spvec_add_spmat [OF _ sorted_spvec_ps], 
          induct v, 
          simp_all add: sorted_spvec.simps)
  }
qed

section{*Equivalence between operations over matrices and sparse matrices.*}

lemma diff_matrix1_zero_eq_zero: "diff_matrix1 0 = 0"
  apply (unfold zero_matrix_def diff_matrix1_def)
  apply (simp add: RepAbs_matrix)
  apply (unfold diff_infmatrix_def) 
  apply (subst Rep_matrix_inject [symmetric])
  apply (simp add: RepAbs_matrix)
  done

lemma foldl_zero_matrix:
  shows "foldl (λ(m::'a matrix) x::nat × 'a. m) (0::'a::lordered_ring matrix) v = 0"
  by (induct v, simp_all)

lemma Rep_Abs_zero_func:
  shows "Rep_matrix (Abs_matrix (λm n::nat. 0::'a::lordered_ring)) p q 
  = (0::'a)"
  apply(fold zero_matrix_def)
  using Rep_zero_matrix_def [of p q] .

lemma is_matrix_i_j:
  shows "Rep_matrix (Abs_matrix (λ(m::nat) n::nat. if i = m ∧ j = n then v else 0)) =
  (λm n. if i = m ∧ j = n then v else (0::'b::lordered_ring))"
  using RepAbs_matrix [of "(λm n. if i = m ∧ j = n then v else 0)"]
  using exI [of _ "i + 1"] using exI [of _ "j + 1"] by auto

lemma diff_matrix1_singleton:
  shows "diff_matrix1 (singleton_matrix i j v) =
  Abs_matrix (λk l. if (k + l) mod 2 = 1 then 0::'a::lordered_ring 
          else if i = k ∧ j = l + 1 then v else 0)"
  unfolding diff_matrix1_def
  unfolding o_apply
  unfolding diff_infmatrix_def
  unfolding singleton_matrix_def
  unfolding is_matrix_i_j
  apply (rule comb [of "Abs_matrix" "Abs_matrix"])
  unfolding expand_fun_eq by auto

lemma comb_matrix:
  assumes f_eq: "f = g" and matrix_eq: "(A::'a matrix) = B"
  and x_eq: "(x::int) = x'" and y_eq: "(y::int) = y'"
  shows "f A x y = g B x' y'"
  using f_eq matrix_eq x_eq y_eq by simp

lemma diff_matrix1_execute_sparse: 
  shows "diff_matrix1 (sparse_row_matrix A) 
  = sparse_row_matrix (differ_spmat' A)"
proof (induct A)
  show "diff_matrix1 (sparse_row_matrix [])
    = sparse_row_matrix (differ_spmat' [])"
    using diff_matrix1_zero_eq_zero by simp
next
  fix a :: "nat × (nat × 'a) list"
  obtain i v where a_split: "a = (i, v)" by force
  fix A :: "'a spmat"
  assume hypo: "diff_matrix1 (sparse_row_matrix A)
    = sparse_row_matrix (differ_spmat' A)"
  show "diff_matrix1 (sparse_row_matrix (a # A))
    = sparse_row_matrix (differ_spmat' (a # A))"
    unfolding a_split
    unfolding sparse_row_matrix_cons [of _ A]
    unfolding fst_conv snd_conv
    unfolding differ_spmat'_dist [of _ _ "A"]
    unfolding sparse_row_add_spmat [of _ "differ_spmat' A"]
    unfolding diff_matrix1_hom [of _ "sparse_row_matrix A"] 
    unfolding hypo apply simp
  proof -
    show "diff_matrix1 (move_matrix (sparse_row_vector v) (int i) 0) =
    sparse_row_matrix
     (map (λ(j, x).
              if j = 0 then (i, [(j, 0::'a)])
              else if (i + j) mod 2 = 0 then (i, [(j - 1, 0::'a)]) else (i, [(j - 1, x)]))
      v)"
      (is "_ = sparse_row_matrix (map ?f v)")
    proof (induct v)
      case Nil 
      show ?case
        unfolding sparse_row_vector_empty
        unfolding move_matrix_zero
        unfolding diff_matrix1_zero_eq_zero
        unfolding map.simps (1)
        unfolding sparse_row_matrix_empty ..
    next
      case Cons
      fix a :: "nat × 'a"
      obtain j x where a_split: "a = (j, x)" by force
      fix v :: "(nat × 'a) list"
      assume hyps: "diff_matrix1 (move_matrix (sparse_row_vector v) (int i) 0) =
          sparse_row_matrix (map ?f v)"
      show "diff_matrix1 (move_matrix (sparse_row_vector (a # v)) (int i) 0) =
             sparse_row_matrix (map ?f (a # v))"
        unfolding map.simps
        unfolding sparse_row_vector_cons
        unfolding move_matrix_add
        unfolding diff_matrix1_hom
        unfolding sparse_row_matrix_cons
        unfolding hyps apply simp
        unfolding a_split 
        unfolding fst_conv snd_conv
        unfolding split_conv
        unfolding diff_matrix1_def
        unfolding o_apply
        unfolding diff_infmatrix_def
        unfolding move_matrix_def
        apply (rule cong [of "Abs_matrix" "Abs_matrix"])
        apply simp
        apply (auto simp add: expand_fun_eq) 
        apply arith apply arith
        unfolding neg_def by arith
    qed
  qed
qed

lemma hom_oper_matrix1_zero_eq_zero: "hom_oper_matrix1 0 = 0"
  apply (unfold zero_matrix_def hom_oper_matrix1_def)
  apply (simp add: RepAbs_matrix)
  apply (unfold hom_oper_infmatrix_def) 
  apply (subst Rep_matrix_inject [symmetric])
  apply (simp add: RepAbs_matrix expand_fun_eq)
  done

lemma hom_oper_matrix1_singleton: 
  shows "hom_oper_matrix1 (singleton_matrix k l v) =
  Abs_matrix (λi j. if (j = 0) then 0 else 
                       if ((i + j) mod 2 = 0) then 0 else 
                          if (k = i ∧ l = j - (1::nat)) then v 
                              else (0::'b::lordered_ring))"
  unfolding hom_oper_matrix1_def
  unfolding o_apply
  unfolding hom_oper_infmatrix_def
  unfolding singleton_matrix_def
  unfolding is_matrix_i_j ..

lemma hom_oper_matrix1_execute_sparse:
  shows "hom_oper_matrix1 (sparse_row_matrix A)
  = sparse_row_matrix (hom_oper_spmat' A)"
proof (induct A)
  show "hom_oper_matrix1 (sparse_row_matrix [])
    = sparse_row_matrix (hom_oper_spmat' [])"
    using hom_oper_matrix1_zero_eq_zero by simp
next
  fix a :: "nat × (nat × 'a) list"
  obtain i v where a_split: "a = (i, v)" by force
  fix A :: "'a spmat"
  assume hypo: "hom_oper_matrix1 (sparse_row_matrix A)
    = sparse_row_matrix (hom_oper_spmat' A)"
  show "hom_oper_matrix1 (sparse_row_matrix (a # A))
    = sparse_row_matrix (hom_oper_spmat' (a # A))"
    unfolding a_split
    unfolding sparse_row_matrix_cons [of _ A]
    unfolding fst_conv snd_conv
    unfolding hom_oper_spmat'_dist [of _ _ "A"]
    unfolding sparse_row_add_spmat [of _ "hom_oper_spmat' A"]
    unfolding hom_oper_matrix1_hom [of _ "sparse_row_matrix A"] 
    unfolding hypo apply simp
  proof -
    show "hom_oper_matrix1 (move_matrix (sparse_row_vector v) (int i) (0::int)) =
    sparse_row_matrix
     (map (λ(j::nat, x::'a).
              if (i + j) mod (2::nat) = Suc (0::nat) then (i, [(j + (1::nat), 0::'a)])
              else (i, [(j + (1::nat), x)])) v)"
      (is "_ = sparse_row_matrix (map ?f v)")
    proof (induct v)
      case Nil 
      show ?case
        unfolding sparse_row_vector_empty
        unfolding move_matrix_zero
        unfolding hom_oper_matrix1_zero_eq_zero
        unfolding map.simps (1)
        unfolding sparse_row_matrix_empty ..
    next
      case Cons
      fix a :: "nat × 'a"
      obtain j x where a_split: "a = (j, x)" by force
      fix v :: "(nat × 'a) list"
      assume hyps: " hom_oper_matrix1 (move_matrix (sparse_row_vector v) (int i) (0::int)) =
       sparse_row_matrix
        (map (λ(j::nat, x::'a).
                 if (i + j) mod (2::nat) = Suc (0::nat) then (i, [(j + (1::nat), 0::'a)])
                 else (i, [(j + (1::nat), x)])) v)"
      show "hom_oper_matrix1 (move_matrix (sparse_row_vector (a # v)) (int i) (0::int)) =
       sparse_row_matrix
        (map (λ(j::nat, x::'a).
                 if (i + j) mod (2::nat) = Suc (0::nat) then (i, [(j + (1::nat), 0::'a)])
                 else (i, [(j + (1::nat), x)]))
          (a # v))"
        unfolding map.simps
        unfolding sparse_row_vector_cons
        unfolding move_matrix_add
        unfolding hom_oper_matrix1_hom
        unfolding sparse_row_matrix_cons
        unfolding hyps apply simp
        unfolding a_split
        unfolding fst_conv snd_conv
        unfolding split_conv
        unfolding hom_oper_matrix1_def
        unfolding o_apply
        unfolding hom_oper_infmatrix_def
        unfolding move_matrix_def
        apply (rule cong [of "Abs_matrix" "Abs_matrix"])
        apply simp
        apply (auto simp add: expand_fun_eq)
        apply arith apply arith
        unfolding neg_def apply arith by arith
    qed
  qed
qed

lemma hom_oper_matrix1_execute_sparse_simp: 
  shows "hom_oper_matrix1 (sparse_row_matrix A) 
  = sparse_row_matrix (hom_oper_spmat A)"
  unfolding hom_oper_matrix1_execute_sparse 
  unfolding sym [OF sparse_row_matrix_hom_oper_spmat] ..

lemma pert_matrix1_zero_eq_zero: "pert_matrix1 0 = 0"
  apply (unfold zero_matrix_def pert_matrix1_def, auto)
  apply (simp add: RepAbs_matrix)
  apply (unfold pert_infmatrix_def)
  apply (rule comb [of Abs_matrix Abs_matrix])
  apply simp
  apply (unfold expand_fun_eq, simp)
  done

lemma pert_matrix1_singleton:
  shows "pert_matrix1 (singleton_matrix k l v) =
  Abs_matrix (λi j. if ((i + j) mod 2 = 1) then 0 else
                          if (k = i + (1::nat) ∧ l = j) then v 
                             else (0::'b::lordered_ring))"
  unfolding pert_matrix1_def
  unfolding o_apply
  unfolding pert_infmatrix_def
  unfolding singleton_matrix_def
  unfolding is_matrix_i_j ..

lemma pert_matrix1_execute_sparse:
  shows "pert_matrix1 (sparse_row_matrix A)
  = sparse_row_matrix (pert_spmat' A)"
proof (induct A)
  show "pert_matrix1 (sparse_row_matrix [])
    = sparse_row_matrix (pert_spmat' [])"
    using pert_matrix1_zero_eq_zero by simp
next
  fix a :: "nat × (nat × 'a) list"
  obtain i v where a_split: "a = (i, v)" by force
  fix A :: "'a spmat"
  assume hypo: "pert_matrix1 (sparse_row_matrix A)
    = sparse_row_matrix (pert_spmat' A)"
  show "pert_matrix1 (sparse_row_matrix (a # A))
    = sparse_row_matrix (pert_spmat' (a # A))"
    unfolding a_split
    unfolding sparse_row_matrix_cons [of _ A]
    unfolding fst_conv snd_conv
    unfolding pert_spmat'_dist [of _ _ "A"]
    unfolding sparse_row_add_spmat [of _ "pert_spmat' A"]
    unfolding pert_matrix1_hom [of _ "sparse_row_matrix A"] 
    unfolding hypo apply simp
  proof -
    show " pert_matrix1 (move_matrix (sparse_row_vector v) (int i) (0::int)) =
    sparse_row_matrix
     (map (λ(j::nat, x::'a).
              if i = (0::nat) then (i - (1::nat), [(j, 0::'a)])
              else if (i + j) mod (2::nat) = (0::nat) then (i - (1::nat), [(j, 0::'a)])
                   else (i - (1::nat), [(j, x)])) v)"
      (is "_ = sparse_row_matrix (map ?f v)")
    proof (induct v)
      case Nil
      show ?case
        unfolding sparse_row_vector_empty
        unfolding move_matrix_zero
        unfolding pert_matrix1_zero_eq_zero
        unfolding map.simps (1)
        unfolding sparse_row_matrix_empty ..
    next
      case Cons
      fix a :: "nat × 'a"
      obtain j x where a_split: "a = (j, x)" by force
      fix v :: "(nat × 'a) list"
      assume hyps: "pert_matrix1 (move_matrix (sparse_row_vector v) (int i) 0) =
          sparse_row_matrix (map ?f v)"
      show "pert_matrix1 (move_matrix (sparse_row_vector (a # v)) (int i) 0) =
             sparse_row_matrix (map ?f (a # v))"
        unfolding map.simps
        unfolding sparse_row_vector_cons
        unfolding move_matrix_add
        unfolding pert_matrix1_hom
        unfolding sparse_row_matrix_cons
        unfolding hyps apply simp
        unfolding a_split
        unfolding fst_conv snd_conv
        unfolding split_conv
        unfolding pert_matrix1_def
        unfolding o_apply
        unfolding pert_infmatrix_def
        unfolding move_matrix_def
        apply (rule cong [of "Abs_matrix" "Abs_matrix"])
        apply simp
        apply (auto simp add: expand_fun_eq)
        apply arith apply arith
        unfolding neg_def by arith
    qed
  qed
qed

lemma pert_matrix1_execute_sparse_simp: 
  shows "pert_matrix1 (sparse_row_matrix A) 
  = sparse_row_matrix (pert_spmat_sort A)"
  unfolding pert_matrix1_execute_sparse
  unfolding sym [OF sparse_row_matrix_pert_spmat]
  unfolding sparse_row_matrix_pert_spmat_eq_pert_spmat_sort ..

subsection{*Zero as sparse matrix*}

text{*In order to get the bound of a function we need a notion of a zero matrix, 
  up to which the bound is computed.*}
text{*In sparse matrices there is no single zero matrix, 
  but any matrix containing only zeros will be such.*}
text{*We provide a function that computes the number 
  of non-zero elements of a vector.*}
text{*If the number of non-zero positions of a vector is zero, 
  this should be the null vector.*}

primrec non_zero_spvec :: "'a::zero spvec => nat"
  where "non_zero_spvec [] = 0"
  | "non_zero_spvec (a#b) = (if snd a ≠ 0 then Suc (non_zero_spvec b) 
                                  else non_zero_spvec b)"

lemma non_zero_spvec_ge_0: "(0::nat) ≤ non_zero_spvec a" ..

text{*We have proven the following lemma in terms of sets 
  since automatic methods apply much better than with Lists and functions 
  @{term "op mem"} or @{term "ListMem"}*}

lemma non_zero_spvec_g_0:
  assumes non_zero_a: "non_zero_spvec (a::'a::zero spvec) ≠ 0" 
  shows "∃pair. pair ∈ set a ∧ snd pair ≠ (0::'a::zero)"
proof (rule ccontr)
  assume contrad: "¬ (∃pair. pair ∈ set a ∧ snd pair ≠ (0::'a))"
  have null_comp: "!!pair. pair ∈ set a ==> snd pair = 0"
  proof -
    fix pair :: "nat × 'a"
    assume pair_in_list: "pair ∈ set a"
    show "snd pair = 0"
    proof -
      have "!!b c. (b,c) ∈ set a ==> c = 0"
        using contrad by simp
      then have "(fst pair, snd pair) ∈ set a ==> snd pair = 0" 
        by fast
      with pair_in_list show ?thesis by force
    qed
  qed
  have "non_zero_spvec (a::'a::zero spvec) = 0"
    using null_comp by (induct a) auto
  with non_zero_a show False ..
qed

text{*Based on the previous function for vectors,
  a function for matrices is defined.*}
text{*This function will be later used in the computation
  of the bound of nilpotency of a matrix.*}

primrec non_zero_spmat:: "'a::zero spmat => nat"
  where "non_zero_spmat [] = 0"
  | "non_zero_spmat (a#b) = non_zero_spvec (snd a) + non_zero_spmat b"

lemma non_zero_spmat_g_0:
  assumes non_zero_a: "non_zero_spmat (a::'a::zero spmat) ≠ 0" 
  shows "∃list. list ∈ set a ∧ non_zero_spvec (snd list) ≠ 0"
proof (rule ccontr)
  assume contrad: "¬ (∃list. list ∈ set a
    ∧ non_zero_spvec (snd list) ≠ 0)"
  have null_comp: "!!list. list ∈ set a ==> non_zero_spvec (snd list) = 0"
    using contrad by auto
  show False 
    using null_comp
    using non_zero_a
    using non_zero_spmat.simps by (induct a) auto
qed

lemma non_zero_spvec_impl_sp_rw_vector:
  assumes non_zero_spvec: "non_zero_spvec A = 0"
  shows "sparse_row_vector A = (0::'a::lordered_ring matrix)"
  using non_zero_spvec proof (induct A)
  case Nil
  {
    show ?case by simp
  }
next
  case Cons
  {
    fix a :: "(nat × 'a)"
    fix A :: "(nat × 'a) list"
    assume hypo: "non_zero_spvec A = 0 ==> sparse_row_vector A = 0" 
      and prem: "non_zero_spvec (a # A) = 0"
    show "sparse_row_vector (a # A) = 0"
    proof -
      have nz_A_0: "non_zero_spvec A = (0::nat)"
        using prem unfolding non_zero_spvec.simps
        by (cases "snd a = 0") auto
      have sa_0: "snd a = 0"
        using prem unfolding non_zero_spvec.simps
        by (cases "snd a = 0") auto
      have srv: "sparse_row_vector A = 0" using hypo [OF nz_A_0] .
      show ?thesis
        unfolding sparse_row_vector_cons
        unfolding srv
        using prem
        unfolding non_zero_spvec.simps (2)
        unfolding sa_0 
        unfolding singleton_matrix_zero [of 0 "fst a"] by simp
    qed
  }
qed

lemma Rep_matrix_inject_exp: 
  shows "(A = B) = (∀j i. Rep_matrix A j i = Rep_matrix B j i)"
  unfolding sym [OF Rep_matrix_inject [of "A" "B"]]
  unfolding expand_fun_eq by simp

lemma move_matrix_zero_le2:
  assumes j: "0 ≤ j" and i: "0 ≤ i"
  shows "(0 = move_matrix A j i) = ((0::('a::{order,zero}) matrix) = A)"
  unfolding Rep_matrix_inject_exp [of "0" "move_matrix A j i"]
  unfolding Rep_matrix_inject_exp [of "0" "A"]
  apply (auto simp add: neg_def)
  apply (drule_tac j="ja+(nat j)" and i="ia+(nat i)" in spec2)
  using i j by auto

lemma Abs_matrix_inject3:
  assumes x: "x ∈ matrix" 
  and y: "y ∈ matrix" 
  and eq: "Abs_matrix x = Abs_matrix y"
  shows "x = y"
  using Abs_matrix_inject [OF x y] using eq ..

lemma Abs_matrix_ne:
  assumes x: "x ∈ matrix" 
  and y: "y ∈ matrix"
  and eq: "x ≠ y"
  shows "Abs_matrix x ≠ Abs_matrix y"
proof (rule ccontr)
  assume contr: "¬ Abs_matrix x ≠ Abs_matrix y"
  show False using contr Abs_matrix_inject3 [OF x y] eq by simp
qed

lemma singl_mat_zero:
  assumes singl_mat: "singleton_matrix i j x = 0"
  shows "x = (0::'a::zero)"
proof (rule ccontr)
  assume contr: "x ≠ 0"
  have singl_mat_ne: "singleton_matrix i j x ≠ 0"
  proof -
    have "{pos. (if i = fst pos ∧ j = snd pos
      then x else 0) ≠ 0} ⊆ {(i,j)}" using contr by auto
    moreover have "finite {(i, j)}" by simp
    ultimately have "finite {pos. (if i = fst pos ∧ j = snd pos
      then x else 0) ≠ 0}" 
      using finite_subset [of _ "{(i, j)}"] by simp
    then have "(λm n. if i = m ∧ j = n then x else 0) ∈ matrix"
      unfolding matrix_def unfolding nonzero_positions_def
      unfolding mem_Collect_eq .
    moreover have "(λj i. 0::'a) ∈ matrix" 
      unfolding matrix_def unfolding nonzero_positions_def 
      by simp
    moreover have "(λm n. if i = m ∧ j = n then x else 0) ≠ (λj i. 0::'a)"
      using contr by (simp add: expand_fun_eq)
    ultimately have "Abs_matrix (λm n. if i = m ∧ j = n then x else 0) ≠
      Abs_matrix (λj i. 0::'a)" using Abs_matrix_ne by auto
    then show ?thesis
      unfolding singleton_matrix_def
      unfolding zero_matrix_def .
  qed
  thus False using singl_mat ..
qed

lemma zero_impl_sing_mat_zero:
  assumes x_zero: "x = (0::'a::zero)"
  shows "singleton_matrix i j x = 0"
  unfolding singleton_matrix_def
  unfolding zero_matrix_def
  by (rule cong [of "Abs_matrix" "Abs_matrix"]) (simp_all add: expand_fun_eq x_zero)

lemma singl_mat_not_zero:
  assumes singl_mat: "singleton_matrix i j x ≠ 0" 
  shows "x ≠ (0::'a::zero)"
proof (rule ccontr)
  assume contr: "¬ x ≠ (0::'a)"
  show False
    using contr
    using zero_impl_sing_mat_zero [of x i j]
    using singl_mat by fast
qed

text{*The following lemma is the first one where we had to add the 
  sortedness property of vectors.*}
text{*The following lemmas will allow us to relate our definition of the zero
  matrix for the type @{typ "'a spmat"}, i.e., @{term "non_zero_spmat"}, 
  with the one of the zero matrix for type @{typ "'a matrix"}.*}

lemma sparse_row_vector_zero_impl_non_zero_spvec: 
  assumes sp_r: "sparse_row_vector A = 0" 
  and srtd: "sorted_spvec A" 
  shows "non_zero_spvec A = 0"
  using sp_r srtd proof (induct A)
  case Nil
  {
    show ?case by simp
  }
next
  case Cons
  {
    fix a :: "(nat × 'a)"
    fix A :: "(nat × 'a) list"
    assume hyp: "[| sparse_row_vector A = 0; sorted_spvec A |]
      ==> non_zero_spvec A = 0"
      and prem1: "sparse_row_vector (a # A) = 0"
      and prem2: "sorted_spvec (a # A)"
    show "non_zero_spvec (a # A) = 0"
    proof (cases "sparse_row_vector A = 0")
      case True
      {
        have n_z_sp: "non_zero_spvec A = 0"
          using hyp [OF True sorted_spvec_cons1 [OF prem2]] .
        have snd_a_0: "snd a = 0"
          using prem1
          unfolding sparse_row_vector_cons 
          unfolding True
          using singl_mat_zero [of 0 "fst a" "snd a"] by simp
        show ?thesis
          using n_z_sp
          using snd_a_0 non_zero_spvec.simps (2) [of a A] by simp
      }
    next
      case False
      {
        have "0 =  singleton_matrix 0 (fst a) (snd a) + sparse_row_vector A"
          using sparse_row_vector_cons [of a A] prem1 by simp
        also have "… =  singleton_matrix 0 (fst a) (snd a) - (- sparse_row_vector A)" 
          by simp
        finally have "0 =  singleton_matrix 0 (fst a) (snd a) - (- sparse_row_vector A)" 
          by simp
        with eq_iff_diff_eq_0 [of "singleton_matrix 0 (fst a) (snd a)" 
          "- sparse_row_vector A"]
        have "- sparse_row_vector A = singleton_matrix 0 (fst a) (snd a)" 
          by simp
        then have "- (- sparse_row_vector A) = - singleton_matrix 0 (fst a) (snd a)"
          by simp
        then have sp_row_vector_A: "sparse_row_vector A = - (singleton_matrix 0 (fst a) (snd a))" 
          by simp
        have snd_a_not_zero: "snd a ≠ 0"
          using sp_row_vector_A
          using False
          using singl_mat_not_zero [of 0 "fst a" "snd a"] by simp
        have disj_matr: "disj_matrices (sparse_row_vector A) (singleton_matrix 0 (fst a) (snd a))"
          using disj_sparse_row_singleton [of "fst a" "fst a" "snd a" "A" "snd a"]
          using prem2 by simp
        then have False
          unfolding sp_row_vector_A
          using snd_a_not_zero
          unfolding disj_matrices_def apply auto
          using spec2 [of "(λj i. (if j = 0 ∧ fst a = i then snd a else 0) = 0)" 0 "fst a"] 
          by simp
        thus ?thesis ..
      }
    qed
  }
qed

lemma sorted_sparse_matrix_cons2: 
  assumes srtd: "sorted_sparse_matrix (a # b # A)" 
  shows "sorted_sparse_matrix (a # A)"
  using srtd 
  unfolding sorted_sparse_matrix_def
  using sorted_spvec_cons2 [of a b A]
  unfolding sorted_spmat.simps by fast

lemma disj_matrices_A_minus_A: 
  assumes A_not_zero: "(A::'a::lordered_ring matrix) ≠ 0" 
  shows "¬ (disj_matrices A (- A))"
proof -
  have ne: "∃i j. Rep_matrix A i j ≠ 0"
  proof (rule ccontr)
    assume not_ex: "¬ (∃i j. Rep_matrix A i j ≠ (0::'a))"
    then have "∀i j. Rep_matrix A i j = 0" by simp
    then have "Rep_matrix A = (λi j. 0)"
      unfolding expand_fun_eq . 
    then have "Rep_matrix A = Rep_matrix (Abs_matrix (λi j. 0))"
      using sym [OF RepAbs_matrix [of  "(λi j. (0::'a))"]] by auto
    then have "A = Abs_matrix (λi j. 0)"
      using Rep_matrix_inject [of A "Abs_matrix (λi j. 0::'a)"] by fast
    with A_not_zero show False unfolding zero_matrix_def ..
  qed
  then obtain i j where Rep_not_zero: "Rep_matrix A i j ≠ 0" by fast
  then have Rep_minus_A_not_zero: "Rep_matrix (- A) i j ≠ 0" 
    unfolding Rep_minus [of A i j] by simp
  show ?thesis 
    using Rep_not_zero
    using Rep_minus_A_not_zero
    unfolding disj_matrices_def by auto
qed

lemma sorted_sparse_matrix_cons:
  assumes srtd: "sorted_sparse_matrix (a # A)"
  shows "sorted_sparse_matrix A"
  using srtd
  unfolding sorted_sparse_matrix_def
  using sorted_spvec_cons1 [of a A]
  using sorted_spmat.simps (2) [of a A] by fast

text{*The following lemma proves the equivalence between the zero matrix 
  and our definition of zero as a sparse matrix. The lemma requires
  the sparse matrix to be sorted.*}

lemma sparse_row_matrix_zero_eq_non_zero_spmat_zero:
  assumes sorted_sp_mat_A: "sorted_sparse_matrix A"
  shows "(sparse_row_matrix A = (0::'a::lordered_ring matrix))
  = (non_zero_spmat A = (0::nat))"
  using sorted_sp_mat_A proof (induct A)
  case Nil
  {
    show "?case" by simp
  }
next
  case Cons
  {
    fix a :: "nat × 'a spvec"
    fix A :: "'a spmat"
    assume hypo: "sorted_sparse_matrix A ==> 
      (sparse_row_matrix A = 0) = (non_zero_spmat A = 0)"
      and prem: "sorted_sparse_matrix (a # A)"
    show "(sparse_row_matrix (a # A) = 0) = (non_zero_spmat (a # A) = 0)"
    proof (rule iffI)
      show "non_zero_spmat (a # A) = 0 ==> sparse_row_matrix (a # A) = 0"
      proof -
        assume non_zero_spmat: "non_zero_spmat (a # A) = 0"
        show "sparse_row_matrix (a # A) = 0"
        proof (cases "non_zero_spmat A = 0")
          case False
          {
            have False
              using False
              using non_zero_spmat
              using non_zero_spmat.simps
              using non_zero_spvec.simps by simp
            then show ?thesis ..
          }
        next
          case True
          {
            have sp_r_mat_A: "sparse_row_matrix A = 0"
              using True
              using prem
              using hypo
              using sorted_sparse_matrix_cons [of a A] by simp
            have non_zero_a: "non_zero_spvec (snd a) = 0"
              using non_zero_spmat by simp
            have sp_row_zero: "sparse_row_vector (snd a) = 0"
              using non_zero_spvec_impl_sp_rw_vector [OF non_zero_a] .
            show ?thesis
              unfolding sparse_row_matrix_cons [of a A]
              unfolding sp_r_mat_A apply simp
              unfolding sp_row_zero unfolding move_matrix_def
              unfolding Rep_zero_matrix_def
              unfolding zero_matrix_def
              apply (rule cong [of "Abs_matrix" "Abs_matrix"])
              unfolding expand_fun_eq by auto
          }
        qed
      qed
    next
      show "sparse_row_matrix (a # A) = 0 ==> non_zero_spmat (a # A) = 0"
      proof -
        assume sp_row_matrix: "sparse_row_matrix (a # A) = 0"
        have srtd: "sorted_spvec (snd a)" 
          using prem
          unfolding sorted_sparse_matrix_def
          unfolding sorted_spmat.simps (2) by fast
        show "non_zero_spmat (a # A) = 0"
        proof (cases "sparse_row_matrix A = 0")
          case True
          {
            have non_zero_spmat_A: "non_zero_spmat A = 0"
              using hypo [OF sorted_sparse_matrix_cons [OF prem]]
              using True ..
            have m_m: "move_matrix (sparse_row_vector (snd a)) (int (fst a)) 0 = 0"
              using sparse_row_matrix_cons [of a A]
              unfolding True
              unfolding sp_row_matrix by simp
            have sp_ro_vc_0: "(sparse_row_vector (snd a)) = 0"
              using move_matrix_zero_le2 [of "int (fst a)" 0 "sparse_row_vector (snd a)"]
              using m_m by simp
            show "non_zero_spmat (a # A) = 0"
              unfolding non_zero_spmat.simps (2)
              unfolding non_zero_spmat_A
              unfolding sparse_row_vector_zero_impl_non_zero_spvec [OF sp_ro_vc_0 srtd]
              by simp
          }
        next
          case False
          {
            have srtd_spmat: "sorted_spmat (a # A)"
              using prem
              unfolding sorted_sparse_matrix_def ..
            then have srtd_spvec: "sorted_spvec (snd a)"
              unfolding sorted_spmat.simps (2) ..
            have disj_matrices_move_matrix:
              "disj_matrices (move_matrix (sparse_row_vector (snd a))
              (int (fst a)) 0)
              (sparse_row_matrix A)"
              using srtd_spvec proof (induct "snd a")
              case Nil
              {
                show ?case 
                  unfolding sparse_row_vector_empty
                  unfolding move_matrix_zero
                  using disj_matrices_zero1 .
              }
            next
              case Cons
              {
                fix b :: "(nat × 'a)"
                fix v :: "(nat × 'a) list"
                assume hypo: "sorted_spvec v
                  ==> disj_matrices (move_matrix (sparse_row_vector v)
                  (int (fst a)) 0) (sparse_row_matrix A)"
                assume prem_induct: "sorted_spvec (b # v)"
                have disj_matrix: "disj_matrices (move_matrix (sparse_row_vector v)
                  (int (fst a)) 0) (sparse_row_matrix A)"
                  using hypo [OF sorted_spvec_cons1 [OF prem_induct]] .
                show "disj_matrices (move_matrix (sparse_row_vector (b # v))
                  (int (fst a)) 0) (sparse_row_matrix A)"
                  unfolding sparse_row_vector_cons 
                  unfolding move_matrix_add
                  unfolding move_matrix_singleton
                  apply simp 
                  apply (cases "snd b = 0", auto simp add: disj_matrix)
                  unfolding disj_matrices_commute
                  apply (rule disj_matrices_x_add)
                  prefer 2 
                  using disj_matrix 
                  unfolding disj_matrices_commute apply simp
                  using prem proof (induct A)
                  case Nil
                  {
                    show ?case
                      unfolding sparse_row_matrix_empty by simp
                  }
                next
                  case Cons
                  {
                    fix c :: "nat × (nat × 'a) list"
                    fix C :: "'a spmat"
                    assume hypo: "[|snd b ≠ 0; sorted_sparse_matrix (a # C)|] ==> 
                      disj_matrices (sparse_row_matrix C)
                      (singleton_matrix (fst a) (fst b) (snd b))"
                      and snd_b: "snd b ≠ 0"
                      and srtd_hypo: "sorted_sparse_matrix (a # c # C)"
                    show "disj_matrices (sparse_row_matrix (c # C))
                      (singleton_matrix (fst a) (fst b) (snd b))"
                      unfolding sparse_row_matrix_cons
                      unfolding move_matrix_add
                      unfolding disj_matrices_commute
                      apply (rule disj_matrices_x_add)
                    proof -
                      show "disj_matrices (singleton_matrix (fst a) (fst b) (snd b))
                        (sparse_row_matrix C)"
                        using hypo [OF snd_b sorted_sparse_matrix_cons2 [OF srtd_hypo]]
                        unfolding disj_matrices_commute .
                      show "disj_matrices (singleton_matrix (fst a) (fst b) (snd b))
                        (move_matrix (sparse_row_vector (snd c)) (int (fst c)) 0)"
                        using snd_b srtd_hypo
                        unfolding disj_matrices_def 
                        unfolding sorted_sparse_matrix_def 
                        unfolding sorted_spmat.simps
                        using sorted_spvec_cons3 [of a c C] 
                        apply auto unfolding neg_def by arith
                    qed
                  }
                qed
              }
            qed
            have "0 = (move_matrix (sparse_row_vector (snd a)) (int (fst a)) 0) 
              + (sparse_row_matrix A)"
              using sp_row_matrix
              unfolding sparse_row_matrix_cons ..
            also have "… = (move_matrix (sparse_row_vector (snd a)) (int (fst a)) 0) 
              - (- (sparse_row_matrix A))" by simp
            finally have "0 = (move_matrix (sparse_row_vector (snd a)) (int (fst a)) 0) 
              - (- (sparse_row_matrix A))" by simp
            then have "- sparse_row_matrix A 
              = (move_matrix (sparse_row_vector (snd a)) (int (fst a)) 0)"
              using eq_iff_diff_eq_0 [of "(move_matrix (sparse_row_vector (snd a))
              (int (fst a)) 0)" "- sparse_row_matrix A"] by simp
            then have "- (- sparse_row_matrix A) =
              - (move_matrix (sparse_row_vector (snd a)) (int (fst a)) 0)"
              by simp
            then have sp_minus_move: "sparse_row_matrix A 
              = - (move_matrix (sparse_row_vector (snd a)) (int (fst a)) 0)"
              by simp
            have False
              using disj_matrices_A_minus_A [OF False]
              unfolding disj_matrices_commute [of "sparse_row_matrix A"]
              using disj_matrices_move_matrix
              unfolding sp_minus_move by simp
            then show ?thesis ..
          }
        qed
      qed
    qed
  }
qed

text{*The computation of the bound is now made by means of a For loop, 
  where the terminating condition is to get to a null matrix.*}
text{*The process is similar to the one applied in the constructive version in the BPL, 
  but using sparse matrices instead of matrices.*}

definition local_bound_gen_spmat ::
  "(('a::lordered_ring) spmat => 'a spmat) => ('a spmat) => nat => nat"
  where "local_bound_gen_spmat f A n 
  = For (λ y. non_zero_spmat y ≠ 0) f (λ y n. n + (1::nat)) A n"

definition local_bound_spmat :: 
  "(('a::lordered_ring) spmat => 'a spmat) => ('a spmat) => nat"
  where "local_bound_spmat f A ≡  local_bound_gen_spmat f A 0"

text{*We also need a definition of the summation of the power series 
  of a given matrix up to a given bound.*}

primrec fin_sum_spmat 
  :: "(('a::lordered_ring) spmat => 'a spmat) => ('a spmat) => nat => 'a spmat"
  where "fin_sum_spmat f A 0 = A"
  | "fin_sum_spmat f A (Suc n) = add_spmat ((f^(Suc n)) A, fin_sum_spmat f A n)"

text{*With the previous definitions we are ready to give 
  the definition of @{term "α"} and @{term "Φ"} for sparse matrices.*}

definition α_spmat :: "('a::lordered_ring) spmat => 'a spmat"
  where "α_spmat A = (minus_spmat (pert_spmat_sort (hom_oper_spmat A)))"

text{*The following definition is the one to be 
  later compared to the one of @{term Φ}*}

definition Φ_spmat :: "('a::lordered_ring) spmat => 'a spmat"
  where "Φ_spmat A = fin_sum_spmat α_spmat A (local_bound_spmat α_spmat A)"

definition example_zero :: "int spmat" 
  where "example_zero == [(3::nat, [((3::nat), (5::int))])]"

definition example_one :: "int spmat" 
  where"example_one ==  
    (0::nat,  (0::nat,   5::int) # (2, - 9) # (6, 8) # []) 
  # (1::nat,  (1::nat,   4::int) # (2,   0) # (8, 8) # [])
  # (2::nat,  (2::nat,   6::int) # (3,   7) # (9, 23) # [])
  # (16::nat, (13::nat, -8::int) # (19, 12) # [])
  # (29::nat, (3::nat,   5::int) # (4,   6) # (7, 8) # [])
  # []"

definition example_one' :: "int spmat" 
  where "example_one' ==  (0::nat, (0::nat, 0::int) # (2, 0) # (6, 0) # []) 
  # (1::nat, (1::nat, 0::int) # (2, 0) # (8, 0) # [])
  # (2::nat, (2::nat, 0::int) # (3, 0) # (9, 0) # []) 
  # (29::nat, (3::nat, 0::int) # (4, 0) # (7, 0) # [])
  # []"

lemma alpha_execute_sparse:
  shows "α (sparse_row_matrix (A::'a::lordered_ring spmat)) 
  = sparse_row_matrix (α_spmat A)"
  unfolding α_spmat_def
  unfolding sparse_row_matrix_minus [of "pert_spmat_sort (hom_oper_spmat A)"]
  unfolding sym [OF sparse_row_matrix_pert_spmat_eq_pert_spmat_sort
    [of "(hom_oper_spmat A)"]]
  unfolding sparse_row_matrix_pert_spmat
  unfolding sym [OF pert_matrix1_execute_sparse [of "(hom_oper_spmat A)"]]
  unfolding sparse_row_matrix_hom_oper_spmat
  unfolding sym [OF hom_oper_matrix1_execute_sparse [of A]]
  unfolding α_def unfolding hom_oper_matrix_def unfolding pert_matrix_def ..

lemma fun_matrix_add: 
  shows "((f::'a matrix => 'a matrix)  + g) (A::'a::lordered_ring matrix) = f A + g A" 
  unfolding plus_fun_def by simp

lemma nth_power_execute_sparse:
  fixes f :: "'a matrix => 'a::lordered_ring matrix"
  fixes f_spmat :: "'a spmat => 'a spmat"
  assumes f_exec: "!!A. f (sparse_row_matrix A) 
  = sparse_row_matrix (f_spmat A)"
  shows "(f ^ n) (sparse_row_matrix A)
  = sparse_row_matrix ((f_spmat^n) A)"
proof (induct n)
  case 0
  {
    show "(f^ 0) (sparse_row_matrix A) 
      = sparse_row_matrix ((f_spmat ^ 0) A)" by simp
  }
next
  case Suc
  {
    fix n::nat
    assume hyps: "(f ^ n) (sparse_row_matrix A) 
      = sparse_row_matrix ((f_spmat ^ n) A)"
    show "(f ^ (Suc n)) (sparse_row_matrix A) 
      = sparse_row_matrix ((f_spmat ^ (Suc n)) A)"
      using hyps 
      apply simp
      using f_exec [of "(f_spmat ^ n) A"] by simp
  }
qed

corollary alpha_nth_power_execute_sparse: 
  shows "(α ^ n) (sparse_row_matrix A)
  = sparse_row_matrix ((α_spmat^n) A)"
  using alpha_execute_sparse
  using nth_power_execute_sparse [of α α_spmat] by auto

lemma fin_sum_alpha_up_to_n_execute_sparse: 
  shows "fin_sum α n (sparse_row_matrix A) 
  = sparse_row_matrix (fin_sum_spmat α_spmat A n)"
proof (induct n)
  case 0
  show ?case by simp
next
  case Suc
  fix n
  assume hyps: "fin_sum α n (sparse_row_matrix A) 
    = sparse_row_matrix (fin_sum_spmat α_spmat A n)"
  show "fin_sum α (Suc n) (sparse_row_matrix A) 
    = sparse_row_matrix (fin_sum_spmat α_spmat A (Suc n))"
    unfolding fin_sum.simps(2) [of "α" "n"] 
    unfolding fin_sum_spmat.simps(2)
    unfolding sparse_row_add_spmat
    unfolding fun_matrix_add
    unfolding hyps
    unfolding alpha_nth_power_execute_sparse [of "Suc n" A] ..
qed

lemma fin_sum_up_to_n_execute_sparse':
  assumes m_eq_n: "m = n" 
  shows "fin_sum α m (sparse_row_matrix A) 
  = sparse_row_matrix (fin_sum_spmat α_spmat A n)"
  using fin_sum_alpha_up_to_n_execute_sparse m_eq_n by simp

lemma sorted_sparse_matrix_add_spmat:
  assumes srtd_A: "sorted_sparse_matrix A" 
  and srtd_B: "sorted_sparse_matrix B" 
  shows "sorted_sparse_matrix (add_spmat (A, B))"
  using srtd_A srtd_B 
  unfolding sorted_sparse_matrix_def 
  using sorted_spvec_add_spmat [of A B]
  using sorted_spmat_add_spmat [of A B] by simp

text{*As we have proved, the @{text "hom_oper_spmat"} operation preserves order*}

lemma "sorted_spvec (α_spmat example_one)"
  unfolding α_spmat_def
  unfolding example_one_def
  apply (simp only: hom_oper_spmat.simps Let_def)
  apply (simp del: pert_spmat.simps minus_spmat.simps)
  apply (simp only: pert_spmat.simps Let_def)
  apply (simp del: minus_spmat.simps)
  apply (simp del:sorted_spvec.simps)
  unfolding sorted_spvec.simps by simp

lemma sorted_spvec_alpha_spmat:
  assumes srtd: "sorted_spvec A"
  shows "sorted_spvec (α_spmat A)"
  unfolding α_spmat_def
  using sorted_spvec_minus_spmat
  [OF sorted_spvec_pert_spmat_sort
    [OF sorted_spvec_hom_oper [OF srtd]]] .

lemma sorted_spvec_alpha_spmat_nth:
  assumes srtd: "sorted_spvec A"
  shows "sorted_spvec ((α_spmat^n) A)"
proof (induct n)
  case 0 show ?case by simp
next
  case Suc
  fix n 
  assume hypo: "sorted_spvec ((α_spmat^n) A)"
  show "sorted_spvec ((α_spmat^(Suc n)) A)"
    using sorted_spvec_alpha_spmat [OF hypo] by simp
qed

lemma sorted_spmat_alpha_spmat:
  shows "sorted_spmat (α_spmat A)"
  unfolding α_spmat_def
  using sorted_spmat_minus_spmat 
  [OF sorted_spmat_pert_spmat_sort 
    [of "hom_oper_spmat A"]] .

lemma sorted_spmat_alpha_spmat_nth: 
  assumes srtd_A: "sorted_spmat A"
  shows "sorted_spmat ((α_spmat^n) A)"
proof (induct n)
  case 0
  {
    show ?case 
      using srtd_A by simp
  }
next
  case Suc
  {
    fix n 
    assume hypo: "sorted_spmat ((α_spmat^n) A)"
    show "sorted_spmat ((α_spmat ^ Suc n) A)"
      using sorted_spmat_alpha_spmat by simp
  }
qed

lemma sorted_sparse_matrix_alpha:
  assumes ssm: "sorted_sparse_matrix A"
  shows "sorted_sparse_matrix (α_spmat A)"
  using ssm
  unfolding sorted_sparse_matrix_def
  using sorted_spvec_alpha_spmat [of A]
  using sorted_spmat_alpha_spmat [of A] by fast

lemma sorted_sparse_matrix_alpha_nth:
  assumes ssm: "sorted_sparse_matrix A"
  shows "sorted_sparse_matrix ((α_spmat^n) A)"
  using ssm
  unfolding sorted_sparse_matrix_def
  using sorted_spvec_alpha_spmat_nth [of A]
  using sorted_spmat_alpha_spmat_nth [of A] by fast

subsection{*Equivalence between the local nilpotency bound 
  for sparse matrices and matrices.*}

lemma local_bounded_func_α_matrix:
  shows "local_bounded_func (α::'a::lordered_ring matrix => 'a matrix)"
  unfolding local_bounded_func_def
  using nrows_strict_decr [OF nrows_alpha_decre]
  using nrows_zero_impl_zero by auto

corollary α_matrix_terminates:
  shows "terminates ((λy. y ≠ 0),
  α::'a::lordered_ring matrix => 'a matrix,
  sparse_row_matrix A)"
  using local_bounded_func_α_matrix
  unfolding local_bounded_func_impl_terminates_loop ..

lemma local_bound_spmat:
  shows "local_bound α (sparse_row_matrix A) 
  = (LEAST n. sparse_row_matrix (
  (α_spmat ^ n) (A::'a::lordered_ring spmat)) = (0::'a matrix))"
  unfolding local_bounded_func_impl_local_bound_is_Least
  [OF local_bounded_func_α_matrix, of "sparse_row_matrix A"]
  unfolding alpha_nth_power_execute_sparse ..

lemma local_bound_spmat_termin:
  shows "local_bound α (sparse_row_matrix A) = 
  (LEAST n. sparse_row_matrix (
  (α_spmat ^ n) (A::'a::lordered_ring spmat)) = (0::'a matrix))"
  unfolding local_bound_correct [OF α_matrix_terminates]
  unfolding alpha_nth_power_execute_sparse [of _ A] ..

lemma least_eq: 
  assumes eq: "∀n::nat. f n = g n" 
  shows "(LEAST n. f n) = (LEAST n. g n)" 
  using eq by simp

lemma LEAST_execute_sparse: 
  assumes ssm: "sorted_sparse_matrix A" 
  shows "(LEAST n. sparse_row_matrix 
  ((α_spmat ^ n) (A::'a::lordered_ring spmat)) = (0::'a matrix)) = 
  (LEAST n. non_zero_spmat 
  ((α_spmat ^ n) (A::'a::lordered_ring spmat)) = (0::nat))"
proof (rule least_eq, rule allI)
  fix n
  show "(sparse_row_matrix ((α_spmat ^ n) A) = 0) 
    = (non_zero_spmat ((α_spmat ^ n) A) = 0)"
    using sparse_row_matrix_zero_eq_non_zero_spmat_zero 
    [OF sorted_sparse_matrix_alpha_nth [OF ssm, of n]] .
qed

lemma local_bound_alpha_spmat:
  assumes ssm: "sorted_sparse_matrix A"
  shows "local_bound α (sparse_row_matrix A)
  = (LEAST n. non_zero_spmat (
  (α_spmat ^ n) (A::'a::lordered_ring spmat)) = (0::nat))"
  unfolding local_bound_spmat
  unfolding LEAST_execute_sparse [OF ssm] ..

text{*Make the simplification rule for @{term "local_bound_gen"}: *}

lemmas local_bound_gen_spmat_simp = 
  For_simp[of "(λ y. non_zero_spmat y ≠ (0::nat))" _ "λ y n. n+(1::nat)", 
  simplified local_bound_gen_spmat_def[symmetric]]

text{*Now, we connect the neccesary termination of 
  For with our termination condition, @{term local_bounded_func}. *}
text{*Then, under the @{term local_bounded_func} premise 
  the loop will be terminating.*}

lemma local_bounded_func_alpha_impl_terminates_loop:
  "local_bounded_func (α::'a matrix => 'a matrix) 
  = (∀ x. terminates (λ y. y ≠ 0, α, (x::'a::lordered_ring matrix)))"
  unfolding local_bounded_func_impl_terminates_loop ..

text{*The following lemma transfers the condition of termination 
  from matrices to sparse matrices.*}

lemma local_bounded_func_alpha_impl_terminates_loop_spmat:
  assumes ssm: "sorted_sparse_matrix (A::'a::lordered_ring spmat)"
  shows "terminates (λy. y ≠ 0, α, sparse_row_matrix A) 
  = terminates (λy. non_zero_spmat y ≠ (0::nat), α_spmat, A)"
  unfolding terminates.simps
  unfolding Orbit_def
  unfolding alpha_nth_power_execute_sparse
  using sparse_row_matrix_zero_eq_non_zero_spmat_zero 
  [OF sorted_sparse_matrix_alpha_nth [OF ssm]] by auto

lemma local_bounded_func_impl_terminates_spmat_loop:
  assumes ssm: "sorted_sparse_matrix (A::'a::lordered_ring spmat)"
  shows "terminates (λ y. non_zero_spmat y ≠ (0::nat), α_spmat, A)"
  using local_bounded_func_α_matrix
  unfolding local_bounded_func_alpha_impl_terminates_loop
  using local_bounded_func_alpha_impl_terminates_loop_spmat [OF ssm]
  by auto

lemma LEAST_local_bound_non_zero_spmat:
  assumes is_zero: "non_zero_spmat A = 0"
  shows "(LEAST n::nat. non_zero_spmat ((α_spmat ^ n) A) = (0::nat)) = 0"
  using Least_le [of "λn. non_zero_spmat ((α_spmat ^n) A) = 0" 0]
  using is_zero by simp

lemma local_bound_gen_spmat_correct:
  "terminates (λ y. non_zero_spmat y ≠ (0::nat), α_spmat, A) 
  ==> local_bound_gen_spmat α_spmat A m 
  = m + (LEAST n::nat. non_zero_spmat ((α_spmat^n) A) = 0)"
  unfolding local_bound_gen_spmat_def
proof (rule For_pinduct 
    [where i=A 
      and s=m 
      and Ac="λ y n. n+1" 
      and continue="λy. non_zero_spmat y ≠ (0::nat)" 
      and f="α_spmat"])
  show "terminates (λy. non_zero_spmat y ≠ 0, α_spmat, A) 
    ==> terminates (λy. non_zero_spmat y ≠ 0, α_spmat, A)" .
next
  fix i s
  assume termin_A: "terminates (λy. non_zero_spmat y ≠ 0, α_spmat, A)"
    and termin_i: "terminates (λy. non_zero_spmat y ≠ 0, α_spmat, i)"
    and hypo: "non_zero_spmat i ≠ 0 ==>
           For (λy. non_zero_spmat y ≠ 0) 
           α_spmat (λy n. n + 1) (α_spmat i) (s + 1) =
           s + 1 + (LEAST n. non_zero_spmat ((α_spmat ^ n) 
           (α_spmat i)) = 0)"
  show "For (λy. non_zero_spmat y ≠ 0) α_spmat (λy n. n + 1) i s 
    = s + (LEAST n. non_zero_spmat ((α_spmat ^ n) i) = 0)"
  proof (cases "non_zero_spmat i = 0")
    case True
    {
      show ?thesis
        unfolding LEAST_local_bound_non_zero_spmat [OF True]
        using True
        using For_simp [of "(λy. non_zero_spmat y ≠ 0)" α_spmat "(λy n. n + 1)" i s]
        by simp
    }
  next
    case False
    {
      from hypo and False
      have For_rew:
        "For (λy. non_zero_spmat y ≠ 0) 
        α_spmat (λy n. n + 1) (α_spmat i) (s + 1) =
        s + 1 + (LEAST n. non_zero_spmat 
        ((α_spmat ^ n) (α_spmat i)) = 0)" by simp
      obtain m where vanish_i: "non_zero_spmat ((α_spmat ^ m) i) = 0" 
        using terminates_implies [OF termin_i] by auto
      have "0 < m"
        using False
        using vanish_i by (cases "m = 0") auto
      from this and vanish_i 
      have vanish_alpha_i: "non_zero_spmat ((α_spmat ^ (m - 1)) (α_spmat i)) = 0"
        using funpow_zip [of "α_spmat" "m - 1" i] by auto
      show ?thesis
        unfolding For_simp [of "(λy. non_zero_spmat y ≠ 0)"
          α_spmat "(λy n. n + 1)" i s]
        unfolding For_rew
        apply (simp add: False)
      proof (rule Least_Suc2[symmetric, of _ m _ "m - 1"])
        show "non_zero_spmat ((α_spmat ^ m) i) = 0" 
          using vanish_i by simp
        show "non_zero_spmat ((α_spmat ^ (m - 1)) (α_spmat i)) = 0"
          using vanish_alpha_i by simp
        show "non_zero_spmat ((α_spmat ^ 0) i) ≠ 0"
          using False by simp
        show "∀k. (non_zero_spmat ((α_spmat ^ Suc k) i) = 0)
          = (non_zero_spmat ((α_spmat ^ k) (α_spmat i)) = 0)"
          using funpow_zip [of α_spmat _ i] by auto
      qed
    }
  qed
qed

text{*The following lemma exactly represents the difference between our 
  old definitions, with which we proved the BPL, and the new ones, 
  from which we are trying to generate code; 
  under the termination premise, both 
  @{term "(LEAST n::nat. (f^n) (x::'a::ab_group_add) = 0)"},
  the old definition of local nilpotency, 
  and @{term [locale=ab_group_add] "local_bound f x"}, the loop computing the lower bound, 
  are equivalent*}
text{*Whereas @{term "(LEAST n::nat. (f^n) (x::'a::ab_group_add) = 0)"} 
  does not have a computable interpretation, 
  @{term [locale=ab_group_add] "local_bound f x"} does have it, 
  and code can be generated from it.*}

lemma local_bound_spmat_correct: 
  "terminates (λ y. non_zero_spmat y ≠ (0::nat), α_spmat, A) ==>
  local_bound_spmat α_spmat A = (LEAST n::nat. non_zero_spmat ((α_spmat^n) A) = 0)"
  unfolding local_bound_spmat_def
  unfolding local_bound_gen_spmat_correct [of A 0] by arith

lemma local_bounded_func_impl_local_bound_spmat_is_Least:
  assumes ssm: "sorted_sparse_matrix (A::'a::lordered_ring spmat)"
  shows "local_bound_spmat α_spmat A 
  = (LEAST n::nat. non_zero_spmat ((α_spmat^n) A) = 0)"
  unfolding local_bound_spmat_correct 
  [OF local_bounded_func_impl_terminates_spmat_loop 
    [OF ssm]] ..

lemma local_bound_eq_local_bound_spmat:
  assumes ssm: "sorted_sparse_matrix (A::'a::lordered_ring spmat)"
  shows "local_bound_spmat α_spmat A 
  = local_bound α (sparse_row_matrix A)"
  unfolding local_bounded_func_impl_local_bound_spmat_is_Least 
  [OF ssm]
  unfolding sym [OF local_bound_alpha_spmat [OF ssm]] ..

lemma phi_execute_sparse:
  assumes ssm: "sorted_sparse_matrix (A::'a::lordered_ring spmat)"
  shows "sparse_row_matrix (Φ_spmat A) 
  = (Φ::'a::lordered_ring matrix => 'a matrix) (sparse_row_matrix A)"
  unfolding Φ_def
  unfolding sym [OF local_bound_eq_local_bound_spmat [OF ssm]]
  unfolding fin_sum_alpha_up_to_n_execute_sparse
  unfolding Φ_spmat_def ..

subsection{*Some examples of code generation.*}

text{*It must be noted that these examples execute the definitions over sparse matrices,
  and are carried out by means of Haftmann',s code generation tool.*}
text{*Consequently, they are not computed directly over the definitions on 
  @{typ "'a matrix"}, which were the ones proved to be an instance of the BPL.*}
text{*Later we will use the HCL to carry out computations with the definitions
  over the type @{typ "'a matrix"}.*}

text{*The value operator is rather slow even for easy examples*}

value example_zero
value "α_spmat example_zero"

definition foos:: "int spmat" 
  where "foos == transpose_spmat [(3::nat, [((4::nat), (5::int))])]"

definition foos1:: "int spmat" 
  where "foos1 == (α_spmat^(1::nat)) example_zero"

definition foos2:: "int spmat" 
  where"foos2 == (α_spmat^(2::nat)) example_zero"

definition foos3:: "int spmat" 
  where "foos3 == (α_spmat^(3::nat)) example_zero"

definition foos4:: "int spmat"
  where "foos4 == (α_spmat^(4::nat)) example_zero"

definition foos5:: "int spmat" 
  where "foos5 == (α_spmat^(5::nat)) example_zero"

definition foos6:: "int spmat" 
  where "foos6 == (α_spmat^(6::nat)) example_zero"

definition foos7:: "int spmat" 
  where "foos7 == (α_spmat^(7::nat)) example_zero"

definition foos_local_bound :: "nat"
  where "foos_local_bound == local_bound_spmat α_spmat example_zero"

definition foos_Phi:: "int spmat"
  where "foos_Phi == Φ_spmat example_zero"

definition goos:: "int spmat"
  where "goos == transpose_spmat example_one"

definition goos1:: "int spmat"
  where "goos1 == (α_spmat^(1::nat)) example_one"

definition goos2:: "int spmat" 
  where "goos2 == (α_spmat^(2::nat)) example_one"

definition goos3:: "int spmat" 
  where "goos3 == (α_spmat^(3::nat)) example_one"

definition goos4:: "int spmat" 
  where "goos4 == (α_spmat^(4::nat)) example_one"

definition goos5:: "int spmat" 
  where "goos5 == (α_spmat^(5::nat)) example_one"

definition goos6:: "int spmat" 
  where "goos6 == (α_spmat^(6::nat)) example_one"

definition goos7:: "int spmat" 
  where "goos7 == (α_spmat^(7::nat)) example_one"

definition goos30:: "int spmat" 
  where "goos30 == (α_spmat^(local_bound_spmat α_spmat example_one)) example_one"

text{*We can also compare a matrix to the zero matrix*}

definition goos30_eq_0:: "bool"
  where "goos30_eq_0 = (non_zero_spmat ((α_spmat^(local_bound_spmat 
  α_spmat example_one)) example_one) = (0::nat))"

definition goos_local_bound::nat
  where "goos_local_bound = local_bound_spmat α_spmat example_one"

definition goos_Phi:: "int spmat" 
  where "goos_Phi == Φ_spmat example_one"

definition blaa :: "int spmat"
  where "blaa == hom_oper_spmat example_one"

definition blaa_square:: "int spmat" 
  where "blaa_square == (hom_oper_spmat^2) example_one"

definition blaa_three:: "int spmat"
  where "blaa_three == (hom_oper_spmat^3) example_one"

definition blaas:: "int spmat" 
  where "blaas == pert_spmat example_one"

definition blaas_square:: "int spmat"
  where "blaas_square == (pert_spmat^2) example_one"

text{*The following are just examples of the code generated from the 
  Isabelle definitions over sparse matrices in order to check
  that they are compatible with the code generation facility.*}

export_code blaa blaa_square blaa_three blaas blaas_square 
  goos goos1 goos2 goos3 goos4 goos5 goos6 goos7 goos30 
  goos30_eq_0 
  goos_local_bound goos_Phi
  foos foos1 foos2 foos3 foos4 foos5 foos6 foos7 
  foos_local_bound foos_Phi 
  in SML module_name "example_Bicomplex" 
  file "example_Bicomplex.ML"

use "example_Bicomplex.ML"
ML "open example_Bicomplex"
ML "blaa"
ML "blaa_square"
ML "blaas"
ML "blaas_square"
ML "blaa_three"
ML "foos"
ML "foos1"
ML "foos2"
ML "foos3"
ML "foos4"
ML "foos5"
ML "foos6"
ML "foos7"
ML "foos_local_bound"
ML "foos_Phi"

ML "goos"
ML "goos1"
ML "goos2"
ML "goos3"
ML "goos4"
ML "goos5"
ML "goos6"
ML "goos7"
ML "goos30"
ML "goos_local_bound"
ML "goos30_eq_0"
ML "goos_Phi"

subsection{*Some lemmas to get code generation in Obua's way*}

text{*In this Section we will pay attention to get 
  to execute the definitions over which
  we have proved the instance before, 
  for instance, @{term "Φ::'a::lordered_ring matrix => 'a matrix"}.*}
text{*Before executing them, we have to provide the HXL with the lemmas that 
  link such definitions with the definitions over sparse matrices, such as 
  @{term α_spmat}.*}

text{*Some preliminar results are also required to get executions in the HCL.*}

lemma neg_int: "neg ((number_of x)::int) = neg x" 
  using number_of_is_id [of x] by simp

lemma split_apply: "split f (a, b) = f a b"
  by simp

definition add_3:: "nat => nat"
  where "add_3 x = x + (3::nat)"

lemma funpow_rec: "f^n = (if n = (0::nat) then id else f o (f^(n - 1)))"
  by (case_tac n) simp_all

lemma funpow_rec_calc:
  "f^(number_of w)
  = (if (number_of w = (0::nat)) then id else f o (f^((number_of w) - 1)))"
by (rule funpow_rec)

lemma comp_calc: "(f o g) x = (f (g x))"
  by auto

lemma id_calc: "id x = x"
  by auto

lemmas compute_iterative = 
  add_3_def 
  funpow_rec_calc 
  comp_calc 
  id_calc
  compute_hol 
  compute_numeral 
  NatBin.mod_nat_number_of 
  NatBin.div_nat_number_of 
  NatBin.nat_number_of

ML{* val computer = 
  PCompute.make Compute.BARRAS @{theory} 
  (ComputeHOL.prep_thms (@{thms "compute_iterative"})) [] 
  fun compute ct = hd (PCompute.rewrite computer [ct])*}

ML{*compute @{cterm "(add_3 (6::nat))"}*}
ML{*compute @{cterm "((add_3^4) (6::nat))"}*}

lemma fin_sum_spmat_rec: 
  "fin_sum_spmat f A n = 
  (if n = 0 then A else add_spmat (((f^n) A), fin_sum_spmat f A (n - 1)))"
proof (cases n)
  case 0
  {
    show ?thesis
      using 0 by simp
  }
next
  case Suc
  {
    obtain m where n_eq: "n = Suc m" 
      using Suc by simp 
    show ?thesis 
      unfolding n_eq 
      unfolding fin_sum_spmat.simps (2) [of f A m] 
      by simp
  }
qed

lemma fin_sum_spmat_rec_calc: 
  "fin_sum_spmat f A (number_of n) = 
  (if (number_of n = (0::nat)) then A 
  else add_spmat (((f^(number_of n)) A), 
  fin_sum_spmat f A ((number_of n) - 1)))"
  unfolding fin_sum_spmat_rec [of f A "number_of n"] ..

lemmas compute_phi_sparse =

  -- "The set of lemmas we use from this theory"
  -- "The lemma computing @{term Φ}"
  sym [OF phi_execute_sparse] 
  Φ_spmat_def

  -- "The lemmas computing the bound"
  sym [OF local_bound_eq_local_bound_spmat] 
  local_bound_spmat_def 
  local_bound_gen_spmat_def 
  For_simp 
  non_zero_spmat.simps 
  non_zero_spvec.simps

  -- "The lemmas dealing with the termination predicate"
  terminates_rec

  -- "The lemmas computing the finite sum of sparse matrices"
  fin_sum_spmat_rec
  fin_sum_spmat_rec_calc

  -- "The lemma computing addition of matrices"
  add_spmat.simps

  -- "The lemmas computing function powers"
  funpow_rec_calc comp_calc 
  id_calc 
  alpha_nth_power_execute_sparse

  -- "The lemmas computing alpha"
  alpha_execute_sparse 
  α_spmat_def
  
  -- "The lemmas computing the differential"
  diff_matrix1_execute_sparse 
  sym [OF sparse_row_matrix_differ_spmat] 
  sparse_row_matrix_differ_spmat_differ_spmat_sort 
  differ_spmat_sort.simps
  
  -- "The lemmas computing the homotopy operator"
  hom_oper_matrix1_execute_sparse_simp 
  hom_oper_spmat.simps 
  
  -- "The lemmas computing the perturbation operator"
  pert_matrix1_execute_sparse_simp 
  pert_spmat_sort.simps
 
  -- "Lemma for the minus operation over matrices"
  minus_spmat.simps
  
  -- "Some aditional library lemmas also required"
  sort_spvec.simps 
  insert_sorted.simps
  compute_hol 
  compute_numeral 
  NatBin.mod_nat_number_of 
  NatBin.div_nat_number_of
  NatBin.nat_number_of 
  neg_int 
  sorted_sp_simps 
  sparse_row_matrix_arith_simps 
  map.simps 
  split_apply

  -- "And of course, some matrices:"
  example_one_def 
  example_one'_def 
  example_zero_def

text{*The BARRAS mode seems to be a more flexible way for code generation, 
  where, for instance, lambda abstractions are allowed in the definitions.*}

text{*Apparently is also slower.*}

ML{* val computer = 
  PCompute.make 
  Compute.BARRAS @{theory} 
  (ComputeHOL.prep_thms (@{thms "compute_phi_sparse"})) [] 
  fun compute ct = hd (PCompute.rewrite computer [ct])*}

ML{*compute 
  @{cterm "terminates (λ y. non_zero_spmat y ≠ (0::nat), 
  α_spmat, 
  example_one)"}*}

ML{*compute 
  @{cterm "(α^(6::nat)) 
  (sparse_row_matrix 
  [(0, [(0, 35::int), (1, 46)]), 
  (1, [(0, 103), (1, 98)]), 
  (23, [(7, 14), (8, 16)])])"}*}

ML{*compute 
  @{cterm "local_bound_spmat α_spmat 
  [(0, [(0, 1::int), (1, 2)]), 
  (2, [(0, 3), (2, 4)])]"}*}

ML{*compute @{cterm 
  "(α^(1::nat)) 
  (sparse_row_matrix 
  [(0, [(0, 1::int), (1, 2)]), 
  (2, [(0, 3), (2, 4)])])"}*}

text{*Different ways to compute the local bound:*}

ML{*compute 
  @{cterm 
  "diff_matrix1 (sparse_row_matrix 
  [(2, [(2, 5::int)])])"}*}

ML{*compute 
  @{cterm 
  "α (sparse_row_matrix [(1, [(2, 5::int)])])"}*}

ML{*compute 
  @{cterm 
  "hom_oper_matrix1 (sparse_row_matrix [(0, [(3, 5::int)])])"}*}

ML{*compute 
  @{cterm 
  "(α^(2::nat)) (sparse_row_matrix [(2, [(1, 5::int)])])"}*}

ML{*compute 
  @{cterm 
  "local_bound α (sparse_row_matrix example_one)"}*}

ML{*compute 
  @{cterm 
  "local_bound α (sparse_row_matrix example_zero)"}*}

ML{*compute 
  @{cterm 
  "local_bound α (sparse_row_matrix example_one')"}*}

ML{*compute 
  @{cterm 
  "(α^(30::nat)) (sparse_row_matrix example_one)"}*}

ML{*compute 
  @{cterm 
  "local_bound_spmat α_spmat example_one"}*}

text{*Different ways to compute the result 
  of applying @{term "Φ"} to the matrix @{term example_one}.*}

ML{*compute @{cterm "Φ_spmat example_one"}*}

ML{*compute @{cterm "Φ (sparse_row_matrix example_one)"}*}

ML{*compute @{cterm 
  "Φ (sparse_row_matrix
  [(0, [(2, 5), (0, - 9), (6, (8::int))]), 
  (1, [(1, 4), (2, 0), (8, 8)]), 
  (2, [(2, 6), (3, 7), (9, 23)]), 
  (29, [(3, 5), (4, 6), (7, 8)])])"}*}

ML{*compute 
  @{cterm 
  "local_bound_spmat α_spmat 
  [(0, [(0, 1::int), (1, 2)]), 
  (1, [(0, 3), (1, 4)])]"}*}

ML{*compute 
  @{cterm 
  "diff_matrix1 (sparse_row_matrix 
  [(0, [(0, 35::int), (1, 46)]), 
  (1, [(0, 103), (1, 98)]), 
  (23, [(7, 14), (8, 16)])])"}*}

text{*Some other experiments making use of the SML mode of the HCL, 
  instead of the BARRAS mode.*}

ML{*val computer = 
  PCompute.make Compute.SML @{theory} 
  (ComputeHOL.prep_thms (@{thms "compute_phi_sparse"})) [] 
  fun compute ct = hd (PCompute.rewrite computer [ct])*}

ML{*compute @{cterm 
  "α_spmat (
  [(0, [(0, 1::int), (1, 2)]), 
  (1, [(0, 3), (1, 4)])])"}*}

ML{*compute 
  @{cterm 
  "local_bound_spmat α_spmat (
  [(0, [(0, 1::int), (1, 2)]), 
  (1, [(0, 3), (1, 4)])])"}*}

ML{*compute 
  @{cterm 
  "α (sparse_row_matrix 
  [(0, [(0, 1::int), (1, 2)]), 
  (1, [(0, 3), (1, 4)])])"}*}

ML{*compute 
  @{cterm 
  "Φ_spmat ([(0, [(0, 1::int), (1, 2)]), 
  (1, [(0, 3), (1, 4)])])"}*}

subsection{*Code generation formt the series @{term "Ψ::'a matrix => 'a::lordered_ring matrix"}*}

text{*The following results will permit us to apply 
  code generation also from the series @{term "Ψ"}.*}
text{*They are similar to the ones already proved for @{term "α"} and @{term "Φ"}.*}

text{*The following definition is the one to be 
  compared with @{term "β::'a::lordered_ring matrix => 'a matrix"}.*}

definition β_spmat :: "('a::lordered_ring) spmat => 'a spmat"
  where "β_spmat A = (minus_spmat (hom_oper_spmat (pert_spmat_sort A)))"

text{*The following definition is the one to be later 
  compared with the one of @{term "Ψ::'a::lordered_ring matrix => 'a matrix"}.*}

definition Ψ_spmat :: "('a::lordered_ring) spmat => 'a spmat"
  where "Ψ_spmat A == fin_sum_spmat β_spmat A (local_bound_spmat β_spmat A)"

lemma beta_equiv: "β = (λA. - (h (δ A)))"
  unfolding β_def
  unfolding fun_Compl_def
  unfolding o_apply ..

lemma sorted_spvec_beta_spmat:
  assumes srtd: "sorted_spvec A"
  shows "sorted_spvec (β_spmat A)"
  unfolding β_spmat_def
  using sorted_spvec_minus_spmat
  [OF sorted_spvec_hom_oper
    [OF sorted_spvec_pert_spmat_sort
      [OF srtd]]] .

lemma sorted_spvec_beta_spmat_nth:
  assumes srtd: "sorted_spvec A"
  shows "sorted_spvec ((β_spmat^n) A)"
proof (induct n)
  case 0 show ?case by simp
next
  case Suc
  fix n 
  assume hypo: "sorted_spvec ((β_spmat^n) A)"
  show "sorted_spvec ((β_spmat^(Suc n)) A)"
    using sorted_spvec_beta_spmat [OF hypo] 
    by simp
qed

lemma sorted_spmat_beta_spmat:
  shows "sorted_spmat (β_spmat A)"
  unfolding β_spmat_def
  using sorted_spmat_minus_spmat 
  [OF sorted_spmat_hom_oper
    [OF sorted_spmat_pert_spmat_sort 
      [of A]]] .

lemma sorted_spmat_beta_spmat_nth: 
  assumes srtd_A: "sorted_spmat A" 
  shows "sorted_spmat ((β_spmat^n) A)"
proof (induct n)
  case 0
  {
    show ?case 
      using srtd_A 
      unfolding funpow.simps (1) 
      unfolding id_apply .
  }
next
  case Suc
  {
    fix n :: nat
    assume hypo: "sorted_spmat ((β_spmat^n) A)"
    show "sorted_spmat ((β_spmat ^ Suc n) A)"
      using sorted_spmat_beta_spmat 
      by simp
  }
qed

lemma sorted_sparse_matrix_beta: 
  assumes ssm: "sorted_sparse_matrix A"
  shows "sorted_sparse_matrix (β_spmat A)"
  using ssm unfolding sorted_sparse_matrix_def
  using sorted_spvec_beta_spmat [of A]
  using sorted_spmat_beta_spmat [of A] by simp

lemma sorted_sparse_matrix_beta_nth: 
  assumes ssm: "sorted_sparse_matrix A" 
  shows "sorted_sparse_matrix ((β_spmat^n) A)"
  using ssm
  unfolding sorted_sparse_matrix_def
  using sorted_spvec_beta_spmat_nth [of A]
  using sorted_spmat_beta_spmat_nth [of A] by auto

lemma beta_execute_sparse:
  "β (sparse_row_matrix A) = sparse_row_matrix (β_spmat A)"
  unfolding β_spmat_def
  unfolding sparse_row_matrix_minus [of "hom_oper_spmat (pert_spmat_sort A)"]
  unfolding sparse_row_matrix_hom_oper_spmat
  unfolding sym [OF hom_oper_matrix1_execute_sparse [of ]]
  unfolding sym [OF sparse_row_matrix_pert_spmat_eq_pert_spmat_sort [of ]]
  unfolding sparse_row_matrix_pert_spmat
  unfolding sym [OF pert_matrix1_execute_sparse [of ]]
  unfolding beta_equiv 
  unfolding hom_oper_matrix_def 
  unfolding pert_matrix_def ..

corollary beta_nth_power_execute_sparse: 
  shows "(β ^ n) (sparse_row_matrix A) 
  = sparse_row_matrix ((β_spmat^n) A)"
  using beta_execute_sparse
  using nth_power_execute_sparse [of β β_spmat] by auto

lemma fin_sum_beta_up_to_n_execute_sparse: 
  shows "fin_sum β n (sparse_row_matrix A) 
  = sparse_row_matrix (fin_sum_spmat β_spmat A n)"
proof (induct n)
  case 0
  show ?case by simp
next
  case Suc
  fix n
  assume hyps: "fin_sum β n (sparse_row_matrix A) 
    = sparse_row_matrix (fin_sum_spmat β_spmat A n)"
  show "fin_sum β (Suc n) (sparse_row_matrix A) 
    = sparse_row_matrix (fin_sum_spmat β_spmat A (Suc n))"
    unfolding fin_sum.simps(2) [of β "n"]
    unfolding fin_sum_spmat.simps(2)
    unfolding sparse_row_add_spmat
    unfolding fun_matrix_add
    unfolding hyps
    unfolding beta_nth_power_execute_sparse [of "Suc n" A] ..
qed

lemma local_bound_spmat_beta:
  assumes bounded_β: "local_bounded_func (β::'a::lordered_ring matrix => 'a matrix)"
  shows "local_bound β (sparse_row_matrix (A::'a::lordered_ring spmat)) 
  = (LEAST n. sparse_row_matrix ((β_spmat ^ n) A) = 0)"
  using local_bounded_func_impl_local_bound_is_Least
  [OF bounded_β, of "sparse_row_matrix A"]
  unfolding beta_nth_power_execute_sparse [of _ A] .

lemma LEAST_execute_sparse_beta: 
  assumes ssm: "sorted_sparse_matrix A" 
  shows "(LEAST n. sparse_row_matrix ((β_spmat ^ n) 
  (A::'a::lordered_ring spmat)) 
  = (0::'a matrix)) = 
  (LEAST n. non_zero_spmat ((β_spmat ^ n) A) = (0::nat))"
proof (rule least_eq, rule allI)
  fix n
  show "(sparse_row_matrix ((β_spmat ^ n) A) = 0)
    = (non_zero_spmat ((β_spmat ^ n) A) = 0)"
    using sparse_row_matrix_zero_eq_non_zero_spmat_zero
    [OF sorted_sparse_matrix_beta_nth [OF ssm, of n]] .
qed

lemma local_bound_beta_spmat:  
  assumes bounded_β: "local_bounded_func 
  (β::'a::lordered_ring matrix => 'a matrix)"
  and ssm: "sorted_sparse_matrix A"
  shows "local_bound β (sparse_row_matrix A) 
  = (LEAST n. non_zero_spmat ((β_spmat ^ n) 
  (A::'a::lordered_ring spmat)) = (0::nat))"
  unfolding local_bound_spmat_beta [OF bounded_β]
  unfolding LEAST_execute_sparse_beta [OF ssm] ..

text{*We make the simplification rule for @{term "local_bound_gen"}: *}

text{*Now, we connect the neccesary termination of For 
  with our termination condition, @{term local_bounded_func}. *}

text{*Then, under the @{term local_bounded_func} premise 
  the loop will be terminating.*}

lemma local_bounded_func_beta_impl_terminates_loop:
  "local_bounded_func (β::'a matrix => 'a matrix) 
  = (∀ x. terminates (λ y. y ≠ 0, β, (x::'a::lordered_ring matrix)))"
  unfolding local_bounded_func_impl_terminates_loop ..

text{*The following lemma transfers the condition 
  of termination from matrices to sparse matrices.*}

lemma local_bounded_func_beta_impl_terminates_loop_spmat:
  assumes ssm: "sorted_sparse_matrix (A::'a::lordered_ring spmat)"
  shows "terminates (λy. y ≠ 0, β, sparse_row_matrix A) 
  = terminates (λy. non_zero_spmat y ≠ (0::nat), β_spmat, A)"
  unfolding terminates.simps
  unfolding Orbit_def
  unfolding beta_nth_power_execute_sparse 
  apply simp
  using sparse_row_matrix_zero_eq_non_zero_spmat_zero
  [OF sorted_sparse_matrix_beta_nth [OF ssm]] by auto

lemma local_bounded_func_impl_terminates_beta_spmat_loop:
  assumes ssm: "sorted_sparse_matrix (A::'a::lordered_ring spmat)"
  and locbf: "local_bounded_func (β::'a::lordered_ring matrix => 'a matrix)"
  shows "terminates (λ y. non_zero_spmat y ≠ (0::nat), β_spmat, A)"
  using locbf
  unfolding local_bounded_func_beta_impl_terminates_loop
  using local_bounded_func_beta_impl_terminates_loop_spmat 
  [OF ssm] by simp

lemma LEAST_local_bound_beta_non_zero_spmat: 
  assumes is_zero: "non_zero_spmat A = 0"
  shows "(LEAST n::nat. non_zero_spmat ((β_spmat ^ n) A) 
  = (0::nat)) = 0"
  using Least_le
  [of "λn. non_zero_spmat ((β_spmat ^n) A) = 0" 0]
  using is_zero by simp

lemma local_bound_gen_spmat_beta_correct: 
  shows "terminates (λ y. non_zero_spmat y ≠ (0::nat), β_spmat, A) 
  ==> local_bound_gen_spmat β_spmat A m 
  = m + (LEAST n::nat. non_zero_spmat ((β_spmat^n) A) = 0)"
  unfolding local_bound_gen_spmat_def
proof (rule For_pinduct [
    where i=A 
    and s=m 
    and Ac="λ y n. n+1" 
    and continue="λy. non_zero_spmat y ≠ (0::nat)" 
    and f="β_spmat"])
  show "terminates (λy. non_zero_spmat y ≠ 0, β_spmat, A)
    ==> terminates (λy. non_zero_spmat y ≠ 0, β_spmat, A)" .
next
  fix i s
  assume termin_A: "terminates (λy. non_zero_spmat y ≠ 0, β_spmat, A)"
    and termin_i: "terminates (λy. non_zero_spmat y ≠ 0, β_spmat, i)"
    and hypo: "non_zero_spmat i ≠ 0 ==>
           For (λy. non_zero_spmat y ≠ 0) 
           β_spmat (λy n. n + 1) (β_spmat i) (s + 1) =
           s + 1 + (LEAST n. non_zero_spmat ((β_spmat ^ n) (β_spmat i)) = 0)"
  show "For (λy. non_zero_spmat y ≠ 0) β_spmat (λy n. n + 1) i s 
    = s + (LEAST n. non_zero_spmat ((β_spmat ^ n) i) = 0)"
  proof (cases "non_zero_spmat i = 0")
    case True
    {
      show ?thesis 
        unfolding LEAST_local_bound_beta_non_zero_spmat [OF True] 
        using True
        using For_simp [of "(λy. non_zero_spmat y ≠ 0)" 
          β_spmat "(λy n. n + 1)" i s]
        by simp
    }
  next
    case False
    {
      have For_rew: "For (λy. non_zero_spmat y ≠ 0) β_spmat 
        (λy n. n + 1) (β_spmat i) (s + 1) =
           s + 1 + (LEAST n. non_zero_spmat (
        (β_spmat ^ n) (β_spmat i)) = 0)"
        using hypo
        using False by simp
      obtain m where vanish_i: "non_zero_spmat ((β_spmat ^ m) i) = 0"
        using terminates_implies [OF termin_i] by auto
      have "0 < m" using False vanish_i by (cases "m = 0") auto
      from this and vanish_i 
      have vanish_beta_i: "non_zero_spmat ((β_spmat ^ (m - 1)) 
        (β_spmat i)) = 0"
        using funpow_zip [of "β_spmat" "m - 1" i] by auto
      show ?thesis
        unfolding For_simp [of "(λy. non_zero_spmat y ≠ 0)" 
          β_spmat "(λy n. n + 1)" i s]
        unfolding For_rew 
        apply (simp add: False)
      proof (rule Least_Suc2[symmetric, of _ m _ "m - 1"])
        show "non_zero_spmat ((β_spmat ^ m) i) = 0" 
          using vanish_i by simp
        show " non_zero_spmat ((β_spmat ^ (m - 1)) (β_spmat i)) = 0" 
          using vanish_beta_i by simp
        show "non_zero_spmat ((β_spmat ^ 0) i) ≠ 0" 
          using False by simp
        show "∀k. (non_zero_spmat ((β_spmat ^ Suc k) i) = 0) 
          = (non_zero_spmat ((β_spmat ^ k) (β_spmat i)) = 0)" 
          using funpow_zip [of β_spmat _ i] by auto
      qed
    }
  qed
qed

text{*The following lemma exactly represents the difference 
  between our old definitions, with which we proved the BPL, and the new ones, 
  from which we are trying to generate code; 
  under the termination premise, both 
  @{term "(LEAST n::nat. (f^n) (x::'a::ab_group_add) = 0)"},
  the old definition of local nilpotency, and 
  @{term [locale=ab_group_add] "local_bound f x"}, the loop computing the lower bound, 
  are equivalent*}

text{*Whereas @{term "(LEAST n::nat. (f^n) (x::'a::ab_group_add) = 0)"} 
  does not have a computable interpretation, 
  @{term [locale=ab_group_add] "local_bound f x"} does have it, 
  and code can be generated from it.*}

lemma local_bound_beta_spmat_correct: 
  "terminates (λ y. non_zero_spmat y ≠ (0::nat), β_spmat, A) ==>
  local_bound_spmat β_spmat A 
  = (LEAST n::nat. non_zero_spmat ((β_spmat^n) A) = 0)"
  unfolding local_bound_spmat_def
  using local_bound_gen_spmat_beta_correct [of A 0] by simp

lemma nrows_hom_oper_pert_decre:
  assumes A_not_null: "(A::'a::lordered_ring matrix) ≠ 0"
  shows "nrows ((- (hom_oper_matrix1 o pert_matrix1)) A) < nrows A"
  (is "nrows ((- ?f) A) < nrows A")
proof (cases "(- ?f) A = 0")
  case True
  have fA_0: "nrows ((- ?f) A) = (0::nat)" 
    unfolding True
    using zero_matrix_def_nrows .
  with not_zero_impl_nrows_g_0 [OF A_not_null]
  show ?thesis by arith
next
  case False
  show "nrows ((- ?f) A) < nrows A"
  proof -
    have nrows_A: "nrows A = Suc (Max (fst ` nonzero_positions (Rep_matrix A)))"
      using not_zero_impl_nonzero_pos [OF A_not_null]
      unfolding nrows_def by simp
    have "nrows ((- ?f) A) = Suc (Max (fst ` nonzero_positions (Rep_matrix ((- ?f) A))))"
      using not_zero_impl_nonzero_pos [OF False]
      unfolding nrows_def by simp
    also have "… = Suc (Max (fst ` nonzero_positions (Rep_matrix (?f A))))" 
      unfolding sym [OF nonzero_positions_minus_f [of ?f A]] ..
    also have "… = Suc (Max (fst ` nonzero_positions (((
      hom_oper_infmatrix o pert_infmatrix) o Rep_matrix) A)))"
      unfolding pert_matrix1_def
      unfolding hom_oper_matrix1_def by simp
    finally have nrows_fA: "nrows ((- ?f) A) =
      Suc (Max (fst ` nonzero_positions (((hom_oper_infmatrix 
      o pert_infmatrix) o Rep_matrix) A)))"
      by simp
    have Max_l: "(Max (fst ` nonzero_positions (Rep_matrix (?f A))))
      < Max (fst ` nonzero_positions (Rep_matrix A))"
    proof (rule Max_A_B)
      let ?n = "Max (fst ` nonzero_positions (Rep_matrix (?f A)))"
      show "∃m>?n .(m ∈ (fst ` nonzero_positions (Rep_matrix A)))"
      proof (rule exI [of _ "Suc ?n"], intro conjI, simp)
        have non_empty: "nonzero_positions (((hom_oper_infmatrix 
          o pert_infmatrix) o Rep_matrix) A) ≠ {}"
          using nonzero_positions_minus_f [of ?f A]
          using not_zero_impl_nonzero_pos [OF False]
          unfolding pert_matrix1_def
          unfolding hom_oper_matrix1_def by simp
        have Max_in_set:
          "?n ∈ (fst ` nonzero_positions (((hom_oper_infmatrix 
          o pert_infmatrix) o Rep_matrix) A))"
          using Max_in [OF finite_imageI 
            [OF hom_oper_infmatrix_pert_infmatrix_finite, of "fst" A]]
          using non_empty
          unfolding image_def 
          unfolding pert_matrix1_def hom_oper_matrix1_def
          by auto
        from Max_in_set obtain a
          where A_a_n_0: "(hom_oper_infmatrix (pert_infmatrix (Rep_matrix A))) ?n a ≠ 0"
          unfolding nonzero_positions_def image_def by auto
        with hom_oper_pert_nonzero_incr_rows [of ?n a] 
        have "(Suc ?n, a - 1) ∈ nonzero_positions (Rep_matrix A)"
          unfolding nonzero_positions_def image_def by auto
        then show "Suc ?n ∈ fst ` nonzero_positions (Rep_matrix A)" 
          unfolding image_def [of fst] by force
      qed
      show "finite (fst ` nonzero_positions (Rep_matrix A))"
        using finite_imageI [OF finite_nonzero_positions [of A], of fst] .
      show "finite (fst ` nonzero_positions (Rep_matrix ((hom_oper_matrix1 o pert_matrix1) A)))"
        unfolding o_apply
        using finite_imageI by simp
    qed
    with nrows_A nrows_fA
    show ?thesis
      unfolding pert_matrix1_def hom_oper_matrix1_def by simp
  qed
qed

corollary nrows_beta_decre:
  assumes A_not_null: "(A::'a::lordered_ring matrix) ≠ 0"
  shows "nrows (β A) < nrows A"
  using nrows_hom_oper_pert_decre [OF A_not_null]
  unfolding β_def
  unfolding fun_Compl_def
  unfolding o_apply
  unfolding pert_matrix_def
  unfolding hom_oper_matrix_def .

lemma local_bounded_func_β_matrix:
  shows "local_bounded_func (β::'a::lordered_ring matrix => 'a matrix)"
  unfolding local_bounded_func_def
  using nrows_strict_decr [OF nrows_beta_decre]
  using nrows_zero_impl_zero by auto

lemma local_bounded_func_beta_impl_local_bound_spmat_is_Least: 
  assumes ssm: "sorted_sparse_matrix (A::'a::lordered_ring spmat)"
  shows "local_bound_spmat β_spmat A 
  = (LEAST n::nat. non_zero_spmat ((β_spmat^n) A) = 0)"
  using local_bound_beta_spmat_correct
  [OF local_bounded_func_impl_terminates_beta_spmat_loop 
    [OF ssm local_bounded_func_β_matrix]] .

lemma local_bound_eq_local_bound_spmat_beta:
  assumes ssm: "sorted_sparse_matrix (A::'a::lordered_ring spmat)"
  shows "local_bound_spmat β_spmat A = local_bound β (sparse_row_matrix A)"
  unfolding local_bounded_func_beta_impl_local_bound_spmat_is_Least
  [OF ssm]
  unfolding sym [OF local_bound_beta_spmat
    [OF local_bounded_func_β_matrix ssm]] ..

lemma psi_execute_sparse:
  assumes ssm: "sorted_sparse_matrix (A::'a::lordered_ring spmat)"
  shows "sparse_row_matrix (Ψ_spmat A) 
  = (Ψ::'a::lordered_ring matrix => 'a matrix) (sparse_row_matrix A)"
  unfolding Ψ_def
  unfolding sym 
  [OF local_bound_eq_local_bound_spmat_beta
    [OF ssm]]
  unfolding fin_sum_beta_up_to_n_execute_sparse
  unfolding Ψ_spmat_def ..

lemmas compute_phi_psi_sparse =

  -- "The set of lemmas we use from this theory"
  -- "The lemma computing @{term Φ}"
  sym [OF phi_execute_sparse] 
  Φ_spmat_def

  -- "The lemma computing @{term Ψ}"
  sym [OF psi_execute_sparse] 
  Ψ_spmat_def

  -- "The lemma computing the bound for @{term α}"
  sym [OF local_bound_eq_local_bound_spmat] 

  -- "The lemma computing the bound for @{term β}"
  sym [OF local_bound_eq_local_bound_spmat_beta] 

  -- "The generic lemmas computing the bound"
  local_bound_spmat_def 
  local_bound_gen_spmat_def 
  For_simp 
  non_zero_spmat.simps 
  non_zero_spvec.simps

  -- "The lemmas dealing with the termination predicate"
  terminates_rec

  -- "The lemmas computing the finite sum of sparse matrices"
  fin_sum_spmat_rec 
  fin_sum_spmat_rec_calc

  -- "The lemma computing addition of matrices"
  add_spmat.simps

  -- "The lemmas computing function powers"
  funpow_rec_calc
  comp_calc 
  id_calc
  alpha_nth_power_execute_sparse
  beta_nth_power_execute_sparse
  
  -- "The lemmas computing beta"
  alpha_execute_sparse 
  α_spmat_def
  
  -- "The lemmas computing beta"
  beta_execute_sparse 
  β_spmat_def
  
  -- "The lemmas computing the differential"
  diff_matrix1_execute_sparse 
  sym [OF sparse_row_matrix_differ_spmat] 
  sparse_row_matrix_differ_spmat_differ_spmat_sort 
  differ_spmat_sort.simps
  
  -- "The lemmas computing the homotopy operator"
  hom_oper_matrix1_execute_sparse_simp 
  hom_oper_spmat.simps 
  
  -- "The lemmas computing the perturbation operator"
  pert_matrix1_execute_sparse_simp 
  pert_spmat_sort.simps
 
  -- "Lemma for the minus operation over matrices"
  minus_spmat.simps
  
  -- "Some aditional library lemmas also required"
  sort_spvec.simps 
  insert_sorted.simps
  compute_hol 
  compute_numeral 
  NatBin.mod_nat_number_of 
  NatBin.div_nat_number_of
  NatBin.nat_number_of 
  neg_int 
  sorted_sp_simps 
  sparse_row_matrix_arith_simps 
  map.simps 
  split_apply

  -- "And of course, some matrices:"
  example_one_def
  example_one'_def 
  example_zero_def

text{*The BARRAS mode seems to be a more flexible way for code generation, 
  where, for instance, lambda abstractions are allowed in the definitions.*}

text{*Apparently is also slower.*}

ML{* val computer = 
  PCompute.make 
  Compute.BARRAS @{theory} 
  (ComputeHOL.prep_thms (@{thms "compute_phi_psi_sparse"})) [] 
  fun compute ct = hd (PCompute.rewrite computer [ct])*}

ML{*compute 
  @{cterm "terminates (λ y. non_zero_spmat y ≠ (0::nat), 
  β_spmat,
  example_one)"}*}

ML{*compute 
  @{cterm "(β^(6::nat)) 
  (sparse_row_matrix 
  [(0, [(0, 35::int), (1, 46)]), 
  (1, [(0, 103), (1, 98)]), 
  (23, [(7, 14), (8, 16)])])"}*}

ML{*compute
  @{cterm "local_bound_spmat β_spmat 
  [(0, [(0, 1::int), (1, 2)]), 
  (2, [(0, 3), (2, 4)])]"}*}

ML{*compute @{cterm 
  "(β^(1::nat)) 
  (sparse_row_matrix 
  [(0, [(0, 1::int), (1, 2)]), 
  (2, [(0, 3), (2, 4)])])"}*}

text{*Different ways to compute the local bound:*}

ML{*compute 
  @{cterm 
  "diff_matrix1 (sparse_row_matrix 
  [(2, [(2, 5::int)])])"}*}

ML{*compute
  @{cterm
  "β (sparse_row_matrix [(1, [(2, 5::int)])])"}*}

ML{*compute 
  @{cterm 
  "hom_oper_matrix1 (sparse_row_matrix [(0, [(3, 5::int)])])"}*}

ML{*compute 
  @{cterm 
  "(β^(2::nat)) (sparse_row_matrix [(2, [(1, 5::int)])])"}*}

ML{*compute 
  @{cterm 
  "(α^(2::nat)) (sparse_row_matrix [(2, [(1, 5::int)])])"}*}

ML{*compute
  @{cterm 
  "example_one"}*}

ML{*compute 
  @{cterm 
  "local_bound β (sparse_row_matrix example_one)"}*}

ML{*compute 
  @{cterm 
  "local_bound α (sparse_row_matrix example_one)"}*}

ML{*compute
  @{cterm 
  "local_bound β (sparse_row_matrix example_zero)"}*}

ML{*compute 
  @{cterm 
  "local_bound α (sparse_row_matrix example_one')"}*}

ML{*compute 
  @{cterm
  "(α^(30::nat)) (sparse_row_matrix example_one)"}*}

ML{*compute 
  @{cterm 
  "(β^(29::nat)) (sparse_row_matrix example_one)"}*}

ML{*compute
  @{cterm
  "(β^(30::nat)) (sparse_row_matrix example_one)"}*}

text{*Different ways to compute the result 
  of applying @{term "Φ"} to the matrix @{term example_one}.*}

ML{*compute @{cterm "Φ (sparse_row_matrix example_one)"}*}

ML{*compute @{cterm "Ψ (sparse_row_matrix example_one)"}*}

ML{*compute @{cterm 
  "Φ (sparse_row_matrix
  [(0, [(2, 5), (0, - 9), (6, (8::int))]), 
  (1, [(1, 4), (2, 0), (8, 8)]), 
  (2, [(2, 6), (3, 7), (9, 23)]), 
  (29, [(3, 5), (4, 6), (7, 8)])])"}*}


lemma funpow_equiv: 
  shows "(g o f)^(Suc n) = g o ((f o g)^n) o f"
proof (induct n)
  case 0 show ?case by simp
next
  case Suc
  fix n
  assume hypo: "(g o f) ^ Suc n = g o (f o g) ^ n o f"
  then show "(g o f) ^ Suc (Suc n) = g o (f o g) ^ Suc n o f"
    unfolding funpow.simps (2) [of "g o f" "(Suc n)"]
    by (simp add: o_assoc)
qed

lemma f_id: "f o id = f"
  by simp

lemma fun_Compl_equiv: "- (f o g) = (- f) o g"
  unfolding fun_Compl_def
  unfolding expand_fun_eq
  by simp

lemma funpow_zip_next: 
  "f^(n + 1) = f^n o f"
  using funpow_zip [symmetric, of f n]
  unfolding expand_fun_eq by simp

code_datatype sparse_row_vector sparse_row_matrix

declare zero_matrix_def [code func del]

lemmas [code func] = sparse_row_vector_empty [symmetric]

declare plus_matrix_def [code func del]
lemmas [code func] = sparse_row_add_spmat [symmetric]
lemmas [code func] = sparse_row_vector_add [symmetric]

term "0::'a::zero matrix"

export_code 
  "0::'a::zero matrix" 
  "op +::('a::{plus,zero} matrix => 'a matrix => 'a matrix)"
  in Haskell file -

declare pert_matrix1_def [code func del]

lemmas [code func] = 
  pert_matrix1_execute_sparse_simp 
  pert_spmat_sort.simps

declare hom_oper_matrix1_def [code func del]

lemmas [code func] = 
  hom_oper_matrix1_execute_sparse_simp 
  hom_oper_spmat.simps

declare fun_Compl_def [code func del]

lemmas [code func] = fun_Compl_def

text{*As far as alpha is defined for multiple types, 
  we have to somehow restrict its definition to the appropiate one*}

text{*Thus, we first delete its definition:*}

lemma [code func, code func del]: "α = α" ..

text{*Then we provide a new definition for it with the appropriate data type*}

definition alpha_matrix :: 
  "'a::lordered_ring matrix => 'a matrix" 
  where [code post, code func del]: "alpha_matrix = α"

text{*Then we add the inline tag that will make our 
  definition display each time we use alpha*}

lemmas [code inline] = alpha_matrix_def [symmetric]

text{*And finally we make use of the required lemmas for code generation*}

lemmas [code func] = alpha_execute_sparse [folded alpha_matrix_def]
lemmas [code func] = α_spmat_def

declare Φ_def [code func del]

text{*At the following lemma we cannot go further using code generation's tool 
  by Haftmann, since we have a conditional rule, not an equation.*}

text{*Contrarily, HCL can execute the premises, and succeed with the computation.*}

(*lemmas [code func] = phi_execute_sparse*)

definition "foox = (α :: 'a::lordered_ring matrix => _) o α"

code_thms foox

export_code
  "0::'a::zero matrix" 
  "op +::('a::{plus,zero} matrix => 'a matrix => 'a matrix)" 
  "pert_matrix1"
  "op - ::'a::lordered_ring => 'a => 'a" 
  "hom_oper_matrix1" 
  "alpha_matrix::('a::lordered_ring matrix => 'a matrix)"
  in Haskell file -

end

An example of the BPL based on bicomplexes.

lemma

  - (- x) = x

lemma

  x  (0::'a) ==> - x  (0::'a)

lemma

  - x  (0::'a) ==> x  (0::'a)

lemma nonzero_positions_minus_f:

  nonzero_positions (Rep_matrix (f A)) = nonzero_positions (Rep_matrix ((- f) A))

Definition of the differential.

lemma diff_infmatrix_finite:

  finite (nonzero_positions (diff_infmatrix (Rep_matrix x)))

lemma diff_infmatrix_matrix:

  diff_infmatrix (Rep_matrix x) ∈ matrix

lemma diff_infmatrix_closed:

  Rep_matrix (Abs_matrix (diff_infmatrix (Rep_matrix x))) =
  diff_infmatrix (Rep_matrix x)

lemma diff_infmatrix_is_matrix:

  A ∈ matrix ==> diff_infmatrix A ∈ matrix

lemma diff_infmatrix_twice:

  diff_infmatrix (diff_infmatrix A) = Rep_matrix 0

lemma combine_infmatrix_matrix:

  f (0::'b) (0::'c) = (0::'a)
  ==> combine_infmatrix f (Rep_matrix A) (Rep_matrix B) ∈ matrix

lemma combine_infmatrix_diff_infmatrix_matrix:

  f (0::'b) (0::'c) = (0::'a)
  ==> combine_infmatrix f (diff_infmatrix (Rep_matrix A))
       (diff_infmatrix (Rep_matrix B))
      ∈ matrix

lemma diff_infmatrix_combine_infmatrix_matrix:

  f (0::'b) (0::'c) = (0::'a)
  ==> diff_infmatrix (combine_infmatrix f (Rep_matrix A) (Rep_matrix B)) ∈ matrix

Matrices as an instance of differential group.

lemma Abs_matrix_inject2:

  [| x ∈ matrix; y ∈ matrix; x = y |] ==> Abs_matrix x = Abs_matrix y

lemma diff_matrix1_hom:

  diff_matrix1 (A + B) = diff_matrix1 A + diff_matrix1 B

lemma diff_matrix1_twice:

  diff_matrix1 (diff_matrix1 A) = 0

Definition of the perturbation $\delta_{D}$.

lemma pert_infmatrix_finite:

  finite (nonzero_positions (pert_infmatrix (Rep_matrix x)))

lemma pert_infmatrix_matrix:

  pert_infmatrix (Rep_matrix x) ∈ matrix

lemma pert_infmatrix_closed:

  Rep_matrix (Abs_matrix (pert_infmatrix (Rep_matrix x))) =
  pert_infmatrix (Rep_matrix x)

lemma pert_infmatrix_is_matrix:

  A ∈ matrix ==> pert_infmatrix A ∈ matrix

lemma pert_infmatrix_twice:

  pert_infmatrix (pert_infmatrix A) = Rep_matrix 0

lemma combine_infmatrix_pert_infmatrix_matrix:

  f (0::'b) (0::'c) = (0::'a)
  ==> combine_infmatrix f (pert_infmatrix (Rep_matrix A))
       (pert_infmatrix (Rep_matrix B))
      ∈ matrix

lemma pert_infmatrix_combine_infmatrix_matrix:

  f (0::'b) (0::'c) = (0::'a)
  ==> pert_infmatrix (combine_infmatrix f (Rep_matrix A) (Rep_matrix B)) ∈ matrix

lemma combine_diff_pert_matrix:

  f (0::'a) (0::'a) = (0::'a)
  ==> combine_infmatrix f
       (diff_infmatrix (combine_infmatrix f (Rep_matrix A) (Rep_matrix B)))
       (pert_infmatrix (combine_infmatrix f (Rep_matrix A) (Rep_matrix B)))
      ∈ matrix

lemma combine_diff_pert_matrix_1:

  f (0::'b) (0::'b) = (0::'a)
  ==> combine_infmatrix f (diff_infmatrix (Rep_matrix A))
       (pert_infmatrix (Rep_matrix A))
      ∈ matrix

lemma combine_diff_pert_matrix_2:

  f (0::'a) (0::'a) = (0::'a)
  ==> combine_infmatrix f
       (combine_infmatrix f (diff_infmatrix (Rep_matrix A))
         (pert_infmatrix (Rep_matrix A)))
       (combine_infmatrix f (diff_infmatrix (Rep_matrix B))
         (pert_infmatrix (Rep_matrix B)))
      ∈ matrix

lemma combine_diff_pert_matrix_3:

  f (0::'b) (0::'b) = (0::'a)
  ==> diff_infmatrix
       (combine_infmatrix f (diff_infmatrix (Rep_matrix A))
         (pert_infmatrix (Rep_matrix A)))
      ∈ matrix

lemma combine_diff_pert_matrix_4:

  f (0::'b) (0::'b) = (0::'a)
  ==> pert_infmatrix
       (combine_infmatrix f (diff_infmatrix (Rep_matrix A))
         (pert_infmatrix (Rep_matrix A)))
      ∈ matrix

lemma combine_diff_pert_matrix_5:

  f (0::'a) (0::'a) = (0::'a)
  ==> combine_infmatrix f
       (diff_infmatrix
         (combine_infmatrix f (diff_infmatrix (Rep_matrix A))
           (pert_infmatrix (Rep_matrix A))))
       (pert_infmatrix
         (combine_infmatrix f (diff_infmatrix (Rep_matrix A))
           (pert_infmatrix (Rep_matrix A))))
      ∈ matrix

Matrices as an instance of differential group with a perturbation.

lemma pert_matrix1_hom:

  pert_matrix1 (A + B) = pert_matrix1 A + pert_matrix1 B

lemma pert_matrix1_twice:

  pert_matrix1 (pert_matrix1 A) = 0

lemma diff_matrix1_pert_matrix1_is_zero:

  diff_matrix1 (pert_matrix1 A) = 0

lemma pert_matrix1_diff_matrix1_is_zero:

  pert_matrix1 (diff_matrix1 A) = 0

Definition of the homotopy operator $h$.

lemma hom_oper_infmatrix_finite:

  finite (nonzero_positions (hom_oper_infmatrix (Rep_matrix x)))

lemma hom_oper_infmatrix_matrix:

  hom_oper_infmatrix (Rep_matrix x) ∈ matrix

lemma hom_oper_infmatrix_closed:

  Rep_matrix (Abs_matrix (hom_oper_infmatrix (Rep_matrix x))) =
  hom_oper_infmatrix (Rep_matrix x)

lemma hom_oper_infmatrix_is_matrix:

  A ∈ matrix ==> hom_oper_infmatrix A ∈ matrix

lemma hom_oper_infmatrix_twice:

  hom_oper_infmatrix (hom_oper_infmatrix A) = Rep_matrix 0

lemma combine_infmatrix_hom_oper_infmatrix_matrix:

  f (0::'b) (0::'c) = (0::'a)
  ==> combine_infmatrix f (hom_oper_infmatrix (Rep_matrix A))
       (hom_oper_infmatrix (Rep_matrix B))
      ∈ matrix

lemma hom_oper_infmatrix_combine_infmatrix_matrix:

  f (0::'b) (0::'c) = (0::'a)
  ==> hom_oper_infmatrix (combine_infmatrix f (Rep_matrix A) (Rep_matrix B))
      ∈ matrix

Matrices as an instance of differential group with a homotopy operator.

lemma hom_oper_matrix1_hom:

  hom_oper_matrix1 (A + B) = hom_oper_matrix1 A + hom_oper_matrix1 B

lemma hom_oper_matrix1_twice:

  hom_oper_matrix1 (hom_oper_matrix1 A) = 0

Local nilpotency condition of the previous definitions.

lemma hom_oper_closed:

  Rep_matrix o Abs_matrix o hom_oper_infmatrix o Rep_matrix =
  hom_oper_infmatrix o Rep_matrix

lemma pert_infmatrix_hom_oper_infmatrix_finite:

  finite (nonzero_positions (pert_infmatrix (hom_oper_infmatrix (Rep_matrix x))))

lemma pert_infmatrix_hom_oper_infmatrix_matrix:

  pert_infmatrix (hom_oper_infmatrix (Rep_matrix x)) ∈ matrix

lemma pert_infmatrix_hom_oper_infmatrix_closed:

  Rep_matrix (Abs_matrix (pert_infmatrix (hom_oper_infmatrix (Rep_matrix A)))) =
  pert_infmatrix (hom_oper_infmatrix (Rep_matrix A))

lemma hom_oper_infmatrix_pert_infmatrix_finite:

  finite (nonzero_positions (hom_oper_infmatrix (pert_infmatrix (Rep_matrix x))))

lemma hom_oper_infmatrix_pert_infmatrix_matrix:

  hom_oper_infmatrix (pert_infmatrix (Rep_matrix x)) ∈ matrix

lemma hom_oper_infmatrix_pert_infmatrix_closed:

  Rep_matrix (Abs_matrix (hom_oper_infmatrix (pert_infmatrix (Rep_matrix A)))) =
  hom_oper_infmatrix (pert_infmatrix (Rep_matrix A))

lemma f_1_n:

  f ((f ^ n) a) = (f ^ (n + 1)) a

lemma infinite_descending_chain_false:

  (!!n. g (Suc n) < g n) ==> False

lemma P_strict_decr:

  [| !!A. A  0 ==> P (f A) < P A; P 0 = 0 |] ==> ∃n. P ((f ^ n) A) = 0

lemma nrows_strict_decr:

  (!!A. A  0 ==> nrows (f A) < nrows A) ==> ∃n. nrows ((f ^ n) A) = 0

lemma ncols_strict_decr:

  (!!A. A  0 ==> ncols (f A) < ncols A) ==> ∃n. ncols ((f ^ n) A) = 0

lemma nonzero_empty_set:

  nonzero_positions (Rep_matrix 0) = {}

lemma Rep_matrix_inject2:

  Rep_matrix A = Rep_matrix B ==> A = B

lemma nonzero_imp_zero_matrix:

  nonzero_positions (Rep_matrix A) = {} ==> A = 0

lemma nrows_zero_impl_zero:

  nrows A = 0 ==> A = 0

lemma ncols_zero_impl_zero:

  ncols A = 0 ==> A = 0

lemma not_zero_impl_exist:

  A  0 ==> ∃m n. Rep_matrix A m n  (0::'a)

corollary not_zero_impl_nonzero_pos:

  A  0 ==> nonzero_positions (Rep_matrix A)  {}

corollary not_zero_impl_nrows_g_0:

  A  0 ==> 0 < nrows A

corollary not_zero_impl_ncols_g_0:

  A  0 ==> 0 < ncols A

lemma hom_oper_nonzero_positions:

  [| (a, b + 1) ∈ nonzero_positions (Rep_matrix A); 0 < b; (a + b) mod 2  0 |]
  ==> (a, b + 2) ∈ nonzero_positions (hom_oper_infmatrix (Rep_matrix A))

lemma pert_nonzero_positions:

  [| (a + 1, b) ∈ nonzero_positions (Rep_matrix A); (a + b) mod 2 = 0 |]
  ==> (a, b) ∈ nonzero_positions (pert_infmatrix (Rep_matrix A))

lemma pert_nonzero_incr:

  (a, b) ∈ nonzero_positions (pert_infmatrix (Rep_matrix A))
  ==> (a + 1, b) ∈ nonzero_positions (Rep_matrix A)

lemma pert_hom_oper_nonzero_ncols_g_1:

  (a, b) ∈ nonzero_positions (pert_infmatrix (hom_oper_infmatrix (Rep_matrix A)))
  ==> 1  b

lemma pert_hom_oper_nonzero_incr_rows:

  (a, b) ∈ nonzero_positions (pert_infmatrix (hom_oper_infmatrix (Rep_matrix A)))
  ==> (a + 1, b - 1) ∈ nonzero_positions (Rep_matrix A)

lemma hom_oper_pert_nonzero_incr_rows:

  (a, b) ∈ nonzero_positions (hom_oper_infmatrix (pert_infmatrix (Rep_matrix A)))
  ==> (a + 1, b - 1) ∈ nonzero_positions (Rep_matrix A)

lemma Max_A_B:

  [| ∃m>Max B. mA; finite A; finite B |] ==> Max B < Max A

lemma nrows_pert_decre:

  A  0 ==> nrows ((- pert_matrix1) A) < nrows A

lemma nrows_pert_hom_oper_decre:

  A  0 ==> nrows ((- (pert_matrix1 o hom_oper_matrix1)) A) < nrows A

corollary nrows_alpha_decre:

  A  0 ==> nrows (α A) < nrows A

Matrices as an instance of differential group satisfying the local nilpotency condition.

lemma α_matrix_definition:

  α = - (pert_matrix1 o hom_oper_matrix1)

Definition of the operations over sparse matrices.

lemma differ_spmat_dist:

  differ_spmat ((a, b) # A) =
  add_spmat
   ([(a, map (λ(j, x).
                 if j = 0 then (j, 0::'a)
                 else if (a + j) mod 2 = 0 then (j - 1, 0::'a) else (j - 1, x))
          b)],
    differ_spmat A)

lemma differ_spmat'_dist:

  differ_spmat' ((a, b) # A) =
  add_spmat
   (map (λ(j, x).
            if j = 0 then (a, [(j, 0::'a)])
            else if (a + j) mod 2 = 0 then (a, [(j - 1, 0::'a)])
                 else (a, [(j - 1, x)]))
     b,
    differ_spmat' A)

lemma pert_spmat_dist:

  pert_spmat ((a, b) # A) =
  add_spmat
   ([(a - 1,
      map (λ(j, x).
              if a = 0 then (j, 0::'a)
              else if (a + j) mod 2 = 0 then (j, 0::'a) else (j, x))
       b)],
    pert_spmat A)

lemma pert_spmat'_dist:

  pert_spmat' ((a, b) # A) =
  add_spmat
   (map (λ(j, x).
            if a = 0 then (a - 1, [(j, 0::'a)])
            else if (a + j) mod 2 = 0 then (a - 1, [(j, 0::'a)])
                 else (a - 1, [(j, x)]))
     b,
    pert_spmat' A)

lemma hom_oper_spmat_dist:

  hom_oper_spmat ((a, b) # A) =
  add_spmat
   ([(a, map (λ(j, x). if (a + j) mod 2 = 1 then (j + 1, 0::'a) else (j + 1, x))
          b)],
    hom_oper_spmat A)

lemma hom_oper_spmat'_dist:

  hom_oper_spmat' ((a, b) # A) =
  add_spmat
   (map (λ(j, x).
            if (a + j) mod 2 = 1 then (a, [(j + 1, 0::'a)])
            else (a, [(j + 1, x)]))
     b,
    hom_oper_spmat' A)

Some auxiliary lemmas

lemma map_impl_existence:

  map f l = a # l' ==> ∃a'. a' mem lf a' = a

lemma move_matrix_zero:

  move_matrix 0 i j = 0

lemma sparse_row_matrix_cons2:

  sparse_row_matrix [(i, a # v)] =
  singleton_matrix i (fst a) (snd a) + sparse_row_matrix [(i, v)]

lemma sorted_spvec_trans:

  [| sorted_spvec (a # b); c mem b |] ==> fst a < fst c

Properties of the operation @{text "hom_oper_spmat"}

lemma add_spmat_el:

  add_spmat (a, []) = a

lemma foldl_absorb0':

  foldl (λm x. m + l x) M arr = M + foldl (λm x. m + l x) 0 arr

lemma sparse_row_matrix_hom_oper_spvec:

  sparse_row_matrix (hom_oper_spmat [(i, v)]) =
  sparse_row_matrix (hom_oper_spmat' [(i, v)])

lemma sparse_row_matrix_hom_oper_spmat:

  sparse_row_matrix (hom_oper_spmat A) = sparse_row_matrix (hom_oper_spmat' A)

lemma sorted_spvec_hom_oper:

  sorted_spvec A ==> sorted_spvec (hom_oper_spmat A)

lemma sorted_spvec_singleton:

  sorted_spvec [(i, v)]

lemma sorted_spvec_hom_oper_vec:

  sorted_spvec v
  ==> sorted_spvec
       (map (λ(j, x).
                if (i + j) mod 2 = Suc 0 then (j + 1, 0::'a) else (j + 1, x))
         v)

lemma sorted_spmat_hom_oper:

  sorted_spmat A ==> sorted_spmat (hom_oper_spmat A)

Properties of the function @{term "differ_spmat"}

lemma sparse_row_matrix_differ_spvec:

  sparse_row_matrix (differ_spmat [(i, v)]) =
  sparse_row_matrix (differ_spmat' [(i, v)])

lemma sparse_row_matrix_differ_spmat:

  sparse_row_matrix (differ_spmat A) = sparse_row_matrix (differ_spmat' A)

lemma sorted_spvec_differ:

  sorted_spvec A ==> sorted_spvec (differ_spmat A)

Some ideas to keep sorted vectors inside a matrix

lemma

  sorted_spvec [a]

lemma insert_preserves_sorted:

  sorted_spvec v ==> sorted_spvec (insert_sorted a v)

lemma sort_is_sorted:

  sorted_spvec (sort_spvec v)

lemma insert_pair_add_pair:

  insert_sorted (a, b) v = add_spvec ([(a, b)], v)

lemma differ_spmat_sort_dist:

  differ_spmat_sort ((i, v) # vs) =
  add_spmat
   ([(i, sort_spvec
          (map (λ(j, x).
                   if j = 0 then (j, 0::'a)
                   else if (i + j) mod 2 = 0 then (j - 1, 0::'a) else (j - 1, x))
            v))],
    differ_spmat_sort vs)

lemma sorted_spvec_differ_spmat_sort:

  sorted_spvec A ==> sorted_spvec (differ_spmat_sort A)

lemma sorted_spmat_differ_spmat_sort:

  sorted_spmat (differ_spmat_sort A)

lemma sort_spvec_spmat_impl_sorted_spmat:

  sorted_spmat (sort_spvec_spmat A)

lemma sparse_row_vector_not_sorted:

  sparse_row_vector [a, b] = sparse_row_vector [b, a]

lemma sparse_row_vector_dist:

  sparse_row_vector [(i, j + k)] = sparse_row_vector [(i, j), (i, k)]

lemma insert_sorted_preserves_sparse_row_vector:

  sparse_row_vector (a # v) = sparse_row_vector (insert_sorted a v)

lemma sort_spvec_eq_sparse_row_vector:

  sparse_row_vector v = sparse_row_vector (sort_spvec v)

lemma sort_spvec_eq_sparse_row_matrix:

  sparse_row_matrix [(i, v)] = sparse_row_matrix [(i, sort_spvec v)]

lemma sparse_row_matrix_differ_spmat_differ_spmat_sort:

  sparse_row_matrix (differ_spmat A) = sparse_row_matrix (differ_spmat_sort A)

Properties of the perturbation operator

lemma sparse_row_matrix_pert_spvec:

  sparse_row_matrix (pert_spmat [(i, v)]) =
  sparse_row_matrix (pert_spmat' [(i, v)])

lemma sparse_row_matrix_pert_spmat:

  sparse_row_matrix (pert_spmat A) = sparse_row_matrix (pert_spmat' A)

lemma pert_spmat_sort_dist:

  pert_spmat_sort ((i, v) # vs) =
  add_spmat
   ([(i - 1,
      sort_spvec
       (map (λ(j, x).
                if i = 0 then (j, 0::'a)
                else if (i + j) mod 2 = 0 then (j, 0::'a) else (j, x))
         v))],
    pert_spmat_sort vs)

lemma sorted_spmat_pert_spmat_sort:

  sorted_spmat (pert_spmat_sort A)

lemma sparse_row_matrix_pert_spmat_eq_pert_spmat_sort:

  sparse_row_matrix (pert_spmat A) = sparse_row_matrix (pert_spmat_sort A)

lemma sorted_spvec_pert_spmat_sort:

  sorted_spvec A ==> sorted_spvec (pert_spmat_sort A)

lemma sorted_spvec_pert:

  sorted_spvec A ==> sorted_spvec (pert_spmat A)

Equivalence between operations over matrices and sparse matrices.

lemma diff_matrix1_zero_eq_zero:

  diff_matrix1 0 = 0

lemma foldl_zero_matrix:

  foldl (λm x. m) 0 v = 0

lemma Rep_Abs_zero_func:

  Rep_matrix (Abs_matrix (λm n. 0::'a)) p q = (0::'a)

lemma is_matrix_i_j:

  Rep_matrix (Abs_matrix (λm n. if i = mj = n then v else 0::'b)) =
  (λm n. if i = mj = n then v else 0::'b)

lemma diff_matrix1_singleton:

  diff_matrix1 (singleton_matrix i j v) =
  Abs_matrix
   (λk l. if (k + l) mod 2 = 1 then 0::'a
          else if i = kj = l + 1 then v else 0::'a)

lemma comb_matrix:

  [| f = g; A = B; x = x'; y = y' |] ==> f A x y = g B x' y'

lemma diff_matrix1_execute_sparse:

  diff_matrix1 (sparse_row_matrix A) = sparse_row_matrix (differ_spmat' A)

lemma hom_oper_matrix1_zero_eq_zero:

  hom_oper_matrix1 0 = 0

lemma hom_oper_matrix1_singleton:

  hom_oper_matrix1 (singleton_matrix k l v) =
  Abs_matrix
   (λi j. if j = 0 then 0::'b
          else if (i + j) mod 2 = 0 then 0::'b
               else if k = il = j - 1 then v else 0::'b)

lemma hom_oper_matrix1_execute_sparse:

  hom_oper_matrix1 (sparse_row_matrix A) = sparse_row_matrix (hom_oper_spmat' A)

lemma hom_oper_matrix1_execute_sparse_simp:

  hom_oper_matrix1 (sparse_row_matrix A) = sparse_row_matrix (hom_oper_spmat A)

lemma pert_matrix1_zero_eq_zero:

  pert_matrix1 0 = 0

lemma pert_matrix1_singleton:

  pert_matrix1 (singleton_matrix k l v) =
  Abs_matrix
   (λi j. if (i + j) mod 2 = 1 then 0::'b
          else if k = i + 1l = j then v else 0::'b)

lemma pert_matrix1_execute_sparse:

  pert_matrix1 (sparse_row_matrix A) = sparse_row_matrix (pert_spmat' A)

lemma pert_matrix1_execute_sparse_simp:

  pert_matrix1 (sparse_row_matrix A) = sparse_row_matrix (pert_spmat_sort A)

Zero as sparse matrix

lemma non_zero_spvec_ge_0:

  0  non_zero_spvec a

lemma non_zero_spvec_g_0:

  non_zero_spvec a  0 ==> ∃pair. pair ∈ set a ∧ snd pair  (0::'a)

lemma non_zero_spmat_g_0:

  non_zero_spmat a  0 ==> ∃list. list ∈ set anon_zero_spvec (snd list)  0

lemma non_zero_spvec_impl_sp_rw_vector:

  non_zero_spvec A = 0 ==> sparse_row_vector A = 0

lemma Rep_matrix_inject_exp:

  (A = B) = (∀j i. Rep_matrix A j i = Rep_matrix B j i)

lemma move_matrix_zero_le2:

  [| 0  j; 0  i |] ==> (0 = move_matrix A j i) = (0 = A)

lemma Abs_matrix_inject3:

  [| x ∈ matrix; y ∈ matrix; Abs_matrix x = Abs_matrix y |] ==> x = y

lemma Abs_matrix_ne:

  [| x ∈ matrix; y ∈ matrix; x  y |] ==> Abs_matrix x  Abs_matrix y

lemma singl_mat_zero:

  singleton_matrix i j x = 0 ==> x = (0::'a)

lemma zero_impl_sing_mat_zero:

  x = (0::'a) ==> singleton_matrix i j x = 0

lemma singl_mat_not_zero:

  singleton_matrix i j x  0 ==> x  (0::'a)

lemma sparse_row_vector_zero_impl_non_zero_spvec:

  [| sparse_row_vector A = 0; sorted_spvec A |] ==> non_zero_spvec A = 0

lemma sorted_sparse_matrix_cons2:

  sorted_sparse_matrix (a # b # A) ==> sorted_sparse_matrix (a # A)

lemma disj_matrices_A_minus_A:

  A  0 ==> ¬ disj_matrices A (- A)

lemma sorted_sparse_matrix_cons:

  sorted_sparse_matrix (a # A) ==> sorted_sparse_matrix A

lemma sparse_row_matrix_zero_eq_non_zero_spmat_zero:

  sorted_sparse_matrix A ==> (sparse_row_matrix A = 0) = (non_zero_spmat A = 0)

lemma alpha_execute_sparse:

  α (sparse_row_matrix A) = sparse_row_matrix (α_spmat A)

lemma fun_matrix_add:

  (f + g) A = f A + g A

lemma nth_power_execute_sparse:

  (!!A. f (sparse_row_matrix A) = sparse_row_matrix (f_spmat A))
  ==> (f ^ n) (sparse_row_matrix A) = sparse_row_matrix ((f_spmat ^ n) A)

corollary alpha_nth_power_execute_sparse:

  (α ^ n) (sparse_row_matrix A) = sparse_row_matrix ((α_spmat ^ n) A)

lemma fin_sum_alpha_up_to_n_execute_sparse:

  fin_sum α n (sparse_row_matrix A) =
  sparse_row_matrix (fin_sum_spmat α_spmat A n)

lemma fin_sum_up_to_n_execute_sparse':

  m = n
  ==> fin_sum α m (sparse_row_matrix A) =
      sparse_row_matrix (fin_sum_spmat α_spmat A n)

lemma sorted_sparse_matrix_add_spmat:

  [| sorted_sparse_matrix A; sorted_sparse_matrix B |]
  ==> sorted_sparse_matrix (add_spmat (A, B))

lemma

  sorted_spvec (α_spmat example_one)

lemma sorted_spvec_alpha_spmat:

  sorted_spvec A ==> sorted_spvec (α_spmat A)

lemma sorted_spvec_alpha_spmat_nth:

  sorted_spvec A ==> sorted_spvec ((α_spmat ^ n) A)

lemma sorted_spmat_alpha_spmat:

  sorted_spmat (α_spmat A)

lemma sorted_spmat_alpha_spmat_nth:

  sorted_spmat A ==> sorted_spmat ((α_spmat ^ n) A)

lemma sorted_sparse_matrix_alpha:

  sorted_sparse_matrix A ==> sorted_sparse_matrix (α_spmat A)

lemma sorted_sparse_matrix_alpha_nth:

  sorted_sparse_matrix A ==> sorted_sparse_matrix ((α_spmat ^ n) A)

Equivalence between the local nilpotency bound for sparse matrices and matrices.

lemma local_bounded_func_α_matrix:

  local_bounded_func α

corollary α_matrix_terminates:

  terminatesy. y  0, α, sparse_row_matrix A)

lemma local_bound_spmat:

  local_bound α (sparse_row_matrix A) =
  (LEAST n. sparse_row_matrix ((α_spmat ^ n) A) = 0)

lemma local_bound_spmat_termin:

  local_bound α (sparse_row_matrix A) =
  (LEAST n. sparse_row_matrix ((α_spmat ^ n) A) = 0)

lemma least_eq:

  n. f n = g n ==> (LEAST n. f n) = (LEAST n. g n)

lemma LEAST_execute_sparse:

  sorted_sparse_matrix A
  ==> (LEAST n. sparse_row_matrix ((α_spmat ^ n) A) = 0) =
      (LEAST n. non_zero_spmat ((α_spmat ^ n) A) = 0)

lemma local_bound_alpha_spmat:

  sorted_sparse_matrix A
  ==> local_bound α (sparse_row_matrix A) =
      (LEAST n. non_zero_spmat ((α_spmat ^ n) A) = 0)

lemma local_bound_gen_spmat_simp:

  For (λy. non_zero_spmat y  0) fy n. n + 1) x ac =
  (if non_zero_spmat x  0
   then For (λy. non_zero_spmat y  0) fy n. n + 1) (f x) (ac + 1) else ac)

lemma local_bounded_func_alpha_impl_terminates_loop:

  local_bounded_func α = (∀x. terminatesy. y  0, α, x))

lemma local_bounded_func_alpha_impl_terminates_loop_spmat:

  sorted_sparse_matrix A
  ==> terminatesy. y  0, α, sparse_row_matrix A) =
      terminatesy. non_zero_spmat y  0, α_spmat, A)

lemma local_bounded_func_impl_terminates_spmat_loop:

  sorted_sparse_matrix A ==> terminatesy. non_zero_spmat y  0, α_spmat, A)

lemma LEAST_local_bound_non_zero_spmat:

  non_zero_spmat A = 0 ==> (LEAST n. non_zero_spmat ((α_spmat ^ n) A) = 0) = 0

lemma local_bound_gen_spmat_correct:

  terminatesy. non_zero_spmat y  0, α_spmat, A)
  ==> local_bound_gen_spmat α_spmat A m =
      m + (LEAST n. non_zero_spmat ((α_spmat ^ n) A) = 0)

lemma local_bound_spmat_correct:

  terminatesy. non_zero_spmat y  0, α_spmat, A)
  ==> local_bound_spmat α_spmat A =
      (LEAST n. non_zero_spmat ((α_spmat ^ n) A) = 0)

lemma local_bounded_func_impl_local_bound_spmat_is_Least:

  sorted_sparse_matrix A
  ==> local_bound_spmat α_spmat A =
      (LEAST n. non_zero_spmat ((α_spmat ^ n) A) = 0)

lemma local_bound_eq_local_bound_spmat:

  sorted_sparse_matrix A
  ==> local_bound_spmat α_spmat A = local_bound α (sparse_row_matrix A)

lemma phi_execute_sparse:

  sorted_sparse_matrix A
  ==> sparse_row_matrix (Φ_spmat A) = Φ (sparse_row_matrix A)

Some examples of code generation.

Some lemmas to get code generation in Obua's way

lemma neg_int:

  neg (number_of x) = neg x

lemma split_apply:

  split f (a, b) = f a b

lemma funpow_rec:

  f ^ n = (if n = 0 then id else f o f ^ (n - 1))

lemma funpow_rec_calc:

  f ^ number_of w = (if number_of w = 0 then id else f o f ^ (number_of w - 1))

lemma comp_calc:

  (f o g) x = f (g x)

lemma id_calc:

  id x = x

lemma compute_iterative:

  add_3 x = x + 3
  f ^ number_of w = (if number_of w = 0 then id else f o f ^ (number_of w - 1))
  (f o g) x = f (g x)
  id x = x
  If True = (λx y. x)
  If False = (λx y. y)
  (¬ True) = False
  (¬ False) = True
  (P ∧ True) = P
  (True ∧ P) = P
  (P ∧ False) = False
  (False ∧ P) = False
  (P ∨ True) = True
  (True ∨ P) = True
  (P ∨ False) = P
  (False ∨ P) = P
  (True --> P) = P
  (P --> True) = True
  (True --> P) = P
  (P --> False) = (¬ P)
  (False --> P) = True
  (False = False) = True
  (True = True) = True
  (False = True) = False
  (True = False) = False
  fst (x, y) = x
  snd (x, y) = y
  ((a, b) = (c, d)) = (a = cb = d)
  prod_case f (x, y) = f x y
  the (Some x) = x
  (None = Some x) = False
  (Some x = None) = False
  (None = None) = True
  (Some x = Some y) = (x = y)
  option_case = (λa f opt. option_case_compute opt a f)
  option_case_compute None = (λa f. a)
  option_case_compute (Some x) = (λa f. f x)
  list_case = (λa f l. list_case_compute l a f)
  list_case_compute [] = (λa f. a)
  list_case_compute (u # v) = (λa f. f u v)
  length [] = 0
  length (x # xs) = 1 + length xs
  (x # xs) ! n = (if n = 0 then x else xs ! (n - 1))
  Let s f == f s
  If True = (λx y. x)
  If False = (λx y. y)
  Let s f == f s
  fst (x, y) = x
  snd (x, y) = y
  ((a, b) = (c, d)) = (a = cb = d)
  prod_case f (x, y) = f x y
  (¬ True) = False
  (¬ False) = True
  (P ∧ True) = P
  (True ∧ P) = P
  (P ∧ False) = False
  (False ∧ P) = False
  (P ∨ True) = True
  (True ∨ P) = True
  (P ∨ False) = P
  (False ∨ P) = P
  (True --> P) = P
  (P --> True) = True
  (True --> P) = P
  (P --> False) = (¬ P)
  (False --> P) = True
  (False = False) = True
  (True = True) = True
  (False = True) = False
  (True = False) = False
  Int.Bit0 Int.Pls = Int.Pls
  Int.Bit1 Int.Min = Int.Min
  iszero Int.Pls = True
  iszero Int.Min = False
  iszero (Int.Bit0 x) = iszero x
  iszero (Int.Bit1 x) = False
  neg Int.Pls = False
  neg Int.Min = True
  neg (Int.Bit0 x) = neg x
  neg (Int.Bit1 x) = neg x
  lezero Int.Pls = True
  lezero Int.Min = True
  lezero (Int.Bit0 x) = lezero x
  lezero (Int.Bit1 x) = neg x
  (Int.Pls = Int.Pls) = True
  (Int.Min = Int.Min) = True
  (Int.Pls = Int.Min) = False
  (Int.Min = Int.Pls) = False
  (Int.Bit0 x = Int.Bit0 y) = (x = y)
  (Int.Bit1 x = Int.Bit1 y) = (x = y)
  (Int.Bit0 x = Int.Bit1 y) = False
  (Int.Bit1 x = Int.Bit0 y) = False
  (Int.Pls = Int.Bit0 x) = (Int.Pls = x)
  (Int.Pls = Int.Bit1 x) = False
  (Int.Min = Int.Bit0 x) = False
  (Int.Min = Int.Bit1 x) = (Int.Min = x)
  (Int.Bit0 x = Int.Pls) = (x = Int.Pls)
  (Int.Bit1 x = Int.Pls) = False
  (Int.Bit0 x = Int.Min) = False
  (Int.Bit1 x = Int.Min) = (x = Int.Min)
  (Int.Pls < Int.Min) = False
  (Int.Pls < Int.Pls) = False
  (Int.Min < Int.Pls) = True
  (Int.Min < Int.Min) = False
  (Int.Bit0 x < Int.Bit0 y) = (x < y)
  (Int.Bit1 x < Int.Bit1 y) = (x < y)
  (Int.Bit0 x < Int.Bit1 y) = (x  y)
  (Int.Bit1 x < Int.Bit0 y) = (x < y)
  (Int.Pls < Int.Bit0 x) = (Int.Pls < x)
  (Int.Pls < Int.Bit1 x) = (Int.Pls  x)
  (Int.Min < Int.Bit0 x) = (Int.Pls  x)
  (Int.Min < Int.Bit1 x) = (Int.Min < x)
  (Int.Bit0 x < Int.Pls) = (x < Int.Pls)
  (Int.Bit1 x < Int.Pls) = (x < Int.Pls)
  (Int.Bit0 x < Int.Min) = (x < Int.Pls)
  (Int.Bit1 x < Int.Min) = (x < Int.Min)
  (Int.Pls  Int.Min) = False
  (Int.Pls  Int.Pls) = True
  (Int.Min  Int.Pls) = True
  (Int.Min  Int.Min) = True
  (Int.Bit0 x  Int.Bit0 y) = (x  y)
  (Int.Bit1 x  Int.Bit1 y) = (x  y)
  (Int.Bit0 x  Int.Bit1 y) = (x  y)
  (Int.Bit1 x  Int.Bit0 y) = (x < y)
  (Int.Pls  Int.Bit0 x) = (Int.Pls  x)
  (Int.Pls  Int.Bit1 x) = (Int.Pls  x)
  (Int.Min  Int.Bit0 x) = (Int.Pls  x)
  (Int.Min  Int.Bit1 x) = (Int.Min  x)
  (Int.Bit0 x  Int.Pls) = (x  Int.Pls)
  (Int.Bit1 x  Int.Pls) = (x < Int.Pls)
  (Int.Bit0 x  Int.Min) = (x < Int.Pls)
  (Int.Bit1 x  Int.Min) = (x  Int.Min)
  Int.succ Int.Pls = Int.Bit1 Int.Pls
  Int.succ Int.Min = Int.Pls
  Int.succ (Int.Bit0 k) = Int.Bit1 k
  Int.succ (Int.Bit1 k) = Int.Bit0 (Int.succ k)
  Int.pred Int.Pls = Int.Min
  Int.pred Int.Min = Int.Bit0 Int.Min
  Int.pred (Int.Bit0 k) = Int.Bit1 (Int.pred k)
  Int.pred (Int.Bit1 k) = Int.Bit0 k
  - Int.Pls = Int.Pls
  - Int.Min = Int.Bit1 Int.Pls
  - Int.Bit0 k = Int.Bit0 (- k)
  - Int.Bit1 k = Int.Bit1 (Int.pred (- k))
  Int.Pls + k = k
  Int.Min + k = Int.pred k
  k + Int.Pls = k
  k + Int.Min = Int.pred k
  Int.Bit0 k + Int.Bit0 l = Int.Bit0 (k + l)
  Int.Bit0 k + Int.Bit1 l = Int.Bit1 (k + l)
  Int.Bit1 k + Int.Bit0 l = Int.Bit1 (k + l)
  Int.Bit1 k + Int.Bit1 l = Int.Bit0 (k + Int.succ l)
  Int.Pls * w = Int.Pls
  Int.Min * k = - k
  x * Int.Pls = Int.Pls
  x * Int.Min = - x
  Int.Bit0 x * y = Int.Bit0 (x * y)
  x * Int.Bit0 y = Int.Bit0 (x * y)
  Int.Bit1 x * Int.Bit1 y = Int.Bit1 (Int.Bit0 (x * y) + x + y)
  0 = Numeral0
  1 = Numeral1
  nat_norm_number_of (number_of w) = (if lezero w then 0 else number_of w)
  Suc (number_of x) = (if neg x then 1 else number_of (Int.succ x))
  number_of x + number_of y =
  (if neg x then number_of y
   else if neg y then number_of x else number_of (x + y))
  number_of x - number_of y =
  (if neg x then 0
   else if neg y then number_of x else nat_norm_number_of (number_of (x + - y)))
  number_of x * number_of y =
  (if neg x then 0 else if neg y then 0 else number_of (x * y))
  (number_of x = number_of y) = (lezero x ∧ lezero yx = y)
  (number_of x < number_of y) = (x < y ∧ ¬ lezero y)
  (number_of x  number_of y) = (y < x --> lezero x)
  natfac n = (if n = 0 then 1 else n * natfac (n - 1))
  - number_of w = number_of (- w)
  number_of v + number_of w = number_of (v + w)
  number_of x - number_of y = number_of (x + - y)
  number_of v * number_of w = number_of (v * w)
  (0::'a) = Numeral0
  (1::'a) = Numeral1
  (number_of x = number_of y) = (x = y)
  (number_of x  number_of y) = (x  y)
  (number_of x < number_of y) = (x < y)
  max a b = (if a  b then b else a)
  min a b = (if a  b then a else b)
  real (number_of v) = (if neg v then 0 else number_of v)
  nat (number_of v) = number_of v
  real (number_of v) = number_of v
  z ^ number_of (Int.Bit0 w) = (let w = z ^ number_of w in w * w)
  z ^ number_of (Int.Bit1 w) =
  (if Numeral0  number_of w then let w = z ^ number_of w in z * w * w
   else Numeral1)
  z ^ Numeral0 = Numeral1
  z ^ -1 = Numeral1
  a div b = fst (divAlg (a, b))
  a mod b = snd (divAlg (a, b))
  divAlg (a, b) =
  (if 0  a
   then if 0  b then posDivAlg a b
        else if a = 0 then 0N else negateSnd (negDivAlg (- a) (- b))
   else if 0 < b then negDivAlg a b else negateSnd (posDivAlg (- a) (- b)))
  adjust b (q, r) = (if 0  r - b then (2 * q + 1, r - b) else (2 * q, r))
  negateSnd (q, r) = (q, - r)
  posDivAlg a b =
  (if a < bb  0 then (0, a) else adjust b (posDivAlg a (2 * b)))
  negDivAlg a b =
  (if 0  a + bb  0 then (-1, a + b) else adjust b (negDivAlg a (2 * b)))
  even Int.Pls = True
  even Int.Min = False
  even (Int.Bit0 x) = True
  even (Int.Bit1 x) = False
  even (number_of w) = even w
  number_of v mod number_of v' =
  (if neg (number_of v) then 0
   else if neg (number_of v') then number_of v
        else nat (number_of v mod number_of v'))
  number_of v div number_of v' =
  (if neg (number_of v) then 0 else nat (number_of v div number_of v'))
  nat (number_of w) = number_of w

lemma fin_sum_spmat_rec:

  fin_sum_spmat f A n =
  (if n = 0 then A else add_spmat ((f ^ n) A, fin_sum_spmat f A (n - 1)))

lemma fin_sum_spmat_rec_calc:

  fin_sum_spmat f A (number_of n) =
  (if number_of n = 0 then A
   else add_spmat ((f ^ number_of n) A, fin_sum_spmat f A (number_of n - 1)))

lemma compute_phi_sparse:

  sorted_sparse_matrix A1
  ==> Φ (sparse_row_matrix A1) = sparse_row_matrix (Φ_spmat A1)
  Φ_spmat A = fin_sum_spmat α_spmat A (local_bound_spmat α_spmat A)
  sorted_sparse_matrix A1
  ==> local_bound α (sparse_row_matrix A1) = local_bound_spmat α_spmat A1
  local_bound_spmat f A == local_bound_gen_spmat f A 0
  local_bound_gen_spmat f A n = For (λy. non_zero_spmat y  0) fy n. n + 1) A n
  For continue f Ac x ac =
  (if continue x then For continue f Ac (f x) (Ac x ac) else ac)
  non_zero_spmat [] = 0
  non_zero_spmat (a # b) = non_zero_spvec (snd a) + non_zero_spmat b
  non_zero_spvec [] = 0
  non_zero_spvec (a # b) =
  (if snd a  (0::'a) then Suc (non_zero_spvec b) else non_zero_spvec b)
  terminates (continue, f, x) =
  (if continue x then terminates (continue, f, f x) else True)
  fin_sum_spmat f A n =
  (if n = 0 then A else add_spmat ((f ^ n) A, fin_sum_spmat f A (n - 1)))
  fin_sum_spmat f A (number_of n) =
  (if number_of n = 0 then A
   else add_spmat ((f ^ number_of n) A, fin_sum_spmat f A (number_of n - 1)))
  add_spmat ([], bs) = bs
  add_spmat (w # x, []) = w # x
  add_spmat (a # as, b # bs) =
  (if fst a < fst b then a # add_spmat (as, b # bs)
   else if fst b < fst a then b # add_spmat (a # as, bs)
        else (fst a, add_spvec (snd a, snd b)) # add_spmat (as, bs))
  f ^ number_of w = (if number_of w = 0 then id else f o f ^ (number_of w - 1))
  (f o g) x = f (g x)
  id x = x
  (α ^ n) (sparse_row_matrix A) = sparse_row_matrix ((α_spmat ^ n) A)
  α (sparse_row_matrix A) = sparse_row_matrix (α_spmat A)
  α_spmat A = minus_spmat (pert_spmat_sort (hom_oper_spmat A))
  diff_matrix1 (sparse_row_matrix A) = sparse_row_matrix (differ_spmat' A)
  sparse_row_matrix (differ_spmat' A1) = sparse_row_matrix (differ_spmat A1)
  sparse_row_matrix (differ_spmat A) = sparse_row_matrix (differ_spmat_sort A)
  differ_spmat_sort [] = []
  differ_spmat_sort ((i, v) # vs) =
  (let m = sort_spvec
            (map (λ(j, x).
                     if j = 0 then (j, 0::'a)
                     else if (i + j) mod 2 = 0 then (j - 1, 0::'a)
                          else (j - 1, x))
              v);
       M = differ_spmat_sort vs
   in add_spmat ([(i, m)], M))
  hom_oper_matrix1 (sparse_row_matrix A) = sparse_row_matrix (hom_oper_spmat A)
  hom_oper_spmat [] = []
  hom_oper_spmat ((i, v) # vs) =
  (let m = map (λ(j, x). if (i + j) mod 2 = 1 then (j + 1, 0::'a) else (j + 1, x))
            v;
       M = hom_oper_spmat vs
   in add_spmat ([(i, m)], M))
  pert_matrix1 (sparse_row_matrix A) = sparse_row_matrix (pert_spmat_sort A)
  pert_spmat_sort [] = []
  pert_spmat_sort ((i, v) # vs) =
  (let m = sort_spvec
            (map (λ(j, x).
                     if i = 0 then (j, 0::'a)
                     else if (i + j) mod 2 = 0 then (j, 0::'a) else (j, x))
              v);
       M = pert_spmat_sort vs
   in add_spmat ([(i - 1, m)], M))
  minus_spmat [] = []
  minus_spmat (a # as) = (fst a, minus_spvec (snd a)) # minus_spmat as
  sort_spvec [] = []
  sort_spvec (a # b) = insert_sorted a (sort_spvec b)
  insert_sorted a [] = [a]
  insert_sorted a (b # c) =
  (if fst a < fst b then a # b # c
   else if fst a = fst b then (fst a, snd a + snd b) # c
        else b # insert_sorted a c)
  If True = (λx y. x)
  If False = (λx y. y)
  (¬ True) = False
  (¬ False) = True
  (P ∧ True) = P
  (True ∧ P) = P
  (P ∧ False) = False
  (False ∧ P) = False
  (P ∨ True) = True
  (True ∨ P) = True
  (P ∨ False) = P
  (False ∨ P) = P
  (True --> P) = P
  (P --> True) = True
  (True --> P) = P
  (P --> False) = (¬ P)
  (False --> P) = True
  (False = False) = True
  (True = True) = True
  (False = True) = False
  (True = False) = False
  fst (x, y) = x
  snd (x, y) = y
  ((a, b) = (c, d)) = (a = cb = d)
  prod_case f (x, y) = f x y
  the (Some x) = x
  (None = Some x) = False
  (Some x = None) = False
  (None = None) = True
  (Some x = Some y) = (x = y)
  option_case = (λa f opt. option_case_compute opt a f)
  option_case_compute None = (λa f. a)
  option_case_compute (Some x) = (λa f. f x)
  list_case = (λa f l. list_case_compute l a f)
  list_case_compute [] = (λa f. a)
  list_case_compute (u # v) = (λa f. f u v)
  length [] = 0
  length (x # xs) = 1 + length xs
  (x # xs) ! n = (if n = 0 then x else xs ! (n - 1))
  Let s f == f s
  If True = (λx y. x)
  If False = (λx y. y)
  Let s f == f s
  fst (x, y) = x
  snd (x, y) = y
  ((a, b) = (c, d)) = (a = cb = d)
  prod_case f (x, y) = f x y
  (¬ True) = False
  (¬ False) = True
  (P ∧ True) = P
  (True ∧ P) = P
  (P ∧ False) = False
  (False ∧ P) = False
  (P ∨ True) = True
  (True ∨ P) = True
  (P ∨ False) = P
  (False ∨ P) = P
  (True --> P) = P
  (P --> True) = True
  (True --> P) = P
  (P --> False) = (¬ P)
  (False --> P) = True
  (False = False) = True
  (True = True) = True
  (False = True) = False
  (True = False) = False
  Int.Bit0 Int.Pls = Int.Pls
  Int.Bit1 Int.Min = Int.Min
  iszero Int.Pls = True
  iszero Int.Min = False
  iszero (Int.Bit0 x) = iszero x
  iszero (Int.Bit1 x) = False
  neg Int.Pls = False
  neg Int.Min = True
  neg (Int.Bit0 x) = neg x
  neg (Int.Bit1 x) = neg x
  lezero Int.Pls = True
  lezero Int.Min = True
  lezero (Int.Bit0 x) = lezero x
  lezero (Int.Bit1 x) = neg x
  (Int.Pls = Int.Pls) = True
  (Int.Min = Int.Min) = True
  (Int.Pls = Int.Min) = False
  (Int.Min = Int.Pls) = False
  (Int.Bit0 x = Int.Bit0 y) = (x = y)
  (Int.Bit1 x = Int.Bit1 y) = (x = y)
  (Int.Bit0 x = Int.Bit1 y) = False
  (Int.Bit1 x = Int.Bit0 y) = False
  (Int.Pls = Int.Bit0 x) = (Int.Pls = x)
  (Int.Pls = Int.Bit1 x) = False
  (Int.Min = Int.Bit0 x) = False
  (Int.Min = Int.Bit1 x) = (Int.Min = x)
  (Int.Bit0 x = Int.Pls) = (x = Int.Pls)
  (Int.Bit1 x = Int.Pls) = False
  (Int.Bit0 x = Int.Min) = False
  (Int.Bit1 x = Int.Min) = (x = Int.Min)
  (Int.Pls < Int.Min) = False
  (Int.Pls < Int.Pls) = False
  (Int.Min < Int.Pls) = True
  (Int.Min < Int.Min) = False
  (Int.Bit0 x < Int.Bit0 y) = (x < y)
  (Int.Bit1 x < Int.Bit1 y) = (x < y)
  (Int.Bit0 x < Int.Bit1 y) = (x  y)
  (Int.Bit1 x < Int.Bit0 y) = (x < y)
  (Int.Pls < Int.Bit0 x) = (Int.Pls < x)
  (Int.Pls < Int.Bit1 x) = (Int.Pls  x)
  (Int.Min < Int.Bit0 x) = (Int.Pls  x)
  (Int.Min < Int.Bit1 x) = (Int.Min < x)
  (Int.Bit0 x < Int.Pls) = (x < Int.Pls)
  (Int.Bit1 x < Int.Pls) = (x < Int.Pls)
  (Int.Bit0 x < Int.Min) = (x < Int.Pls)
  (Int.Bit1 x < Int.Min) = (x < Int.Min)
  (Int.Pls  Int.Min) = False
  (Int.Pls  Int.Pls) = True
  (Int.Min  Int.Pls) = True
  (Int.Min  Int.Min) = True
  (Int.Bit0 x  Int.Bit0 y) = (x  y)
  (Int.Bit1 x  Int.Bit1 y) = (x  y)
  (Int.Bit0 x  Int.Bit1 y) = (x  y)
  (Int.Bit1 x  Int.Bit0 y) = (x < y)
  (Int.Pls  Int.Bit0 x) = (Int.Pls  x)
  (Int.Pls  Int.Bit1 x) = (Int.Pls  x)
  (Int.Min  Int.Bit0 x) = (Int.Pls  x)
  (Int.Min  Int.Bit1 x) = (Int.Min  x)
  (Int.Bit0 x  Int.Pls) = (x  Int.Pls)
  (Int.Bit1 x  Int.Pls) = (x < Int.Pls)
  (Int.Bit0 x  Int.Min) = (x < Int.Pls)
  (Int.Bit1 x  Int.Min) = (x  Int.Min)
  Int.succ Int.Pls = Int.Bit1 Int.Pls
  Int.succ Int.Min = Int.Pls
  Int.succ (Int.Bit0 k) = Int.Bit1 k
  Int.succ (Int.Bit1 k) = Int.Bit0 (Int.succ k)
  Int.pred Int.Pls = Int.Min
  Int.pred Int.Min = Int.Bit0 Int.Min
  Int.pred (Int.Bit0 k) = Int.Bit1 (Int.pred k)
  Int.pred (Int.Bit1 k) = Int.Bit0 k
  - Int.Pls = Int.Pls
  - Int.Min = Int.Bit1 Int.Pls
  - Int.Bit0 k = Int.Bit0 (- k)
  - Int.Bit1 k = Int.Bit1 (Int.pred (- k))
  Int.Pls + k = k
  Int.Min + k = Int.pred k
  k + Int.Pls = k
  k + Int.Min = Int.pred k
  Int.Bit0 k + Int.Bit0 l = Int.Bit0 (k + l)
  Int.Bit0 k + Int.Bit1 l = Int.Bit1 (k + l)
  Int.Bit1 k + Int.Bit0 l = Int.Bit1 (k + l)
  Int.Bit1 k + Int.Bit1 l = Int.Bit0 (k + Int.succ l)
  Int.Pls * w = Int.Pls
  Int.Min * k = - k
  x * Int.Pls = Int.Pls
  x * Int.Min = - x
  Int.Bit0 x * y = Int.Bit0 (x * y)
  x * Int.Bit0 y = Int.Bit0 (x * y)
  Int.Bit1 x * Int.Bit1 y = Int.Bit1 (Int.Bit0 (x * y) + x + y)
  0 = Numeral0
  1 = Numeral1
  nat_norm_number_of (number_of w) = (if lezero w then 0 else number_of w)
  Suc (number_of x) = (if neg x then 1 else number_of (Int.succ x))
  number_of x + number_of y =
  (if neg x then number_of y
   else if neg y then number_of x else number_of (x + y))
  number_of x - number_of y =
  (if neg x then 0
   else if neg y then number_of x else nat_norm_number_of (number_of (x + - y)))
  number_of x * number_of y =
  (if neg x then 0 else if neg y then 0 else number_of (x * y))
  (number_of x = number_of y) = (lezero x ∧ lezero yx = y)
  (number_of x < number_of y) = (x < y ∧ ¬ lezero y)
  (number_of x  number_of y) = (y < x --> lezero x)
  natfac n = (if n = 0 then 1 else n * natfac (n - 1))
  - number_of w = number_of (- w)
  number_of v + number_of w = number_of (v + w)
  number_of x - number_of y = number_of (x + - y)
  number_of v * number_of w = number_of (v * w)
  (0::'a) = Numeral0
  (1::'a) = Numeral1
  (number_of x = number_of y) = (x = y)
  (number_of x  number_of y) = (x  y)
  (number_of x < number_of y) = (x < y)
  max a b = (if a  b then b else a)
  min a b = (if a  b then a else b)
  real (number_of v) = (if neg v then 0 else number_of v)
  nat (number_of v) = number_of v
  real (number_of v) = number_of v
  z ^ number_of (Int.Bit0 w) = (let w = z ^ number_of w in w * w)
  z ^ number_of (Int.Bit1 w) =
  (if Numeral0  number_of w then let w = z ^ number_of w in z * w * w
   else Numeral1)
  z ^ Numeral0 = Numeral1
  z ^ -1 = Numeral1
  a div b = fst (divAlg (a, b))
  a mod b = snd (divAlg (a, b))
  divAlg (a, b) =
  (if 0  a
   then if 0  b then posDivAlg a b
        else if a = 0 then 0N else negateSnd (negDivAlg (- a) (- b))
   else if 0 < b then negDivAlg a b else negateSnd (posDivAlg (- a) (- b)))
  adjust b (q, r) = (if 0  r - b then (2 * q + 1, r - b) else (2 * q, r))
  negateSnd (q, r) = (q, - r)
  posDivAlg a b =
  (if a < bb  0 then (0, a) else adjust b (posDivAlg a (2 * b)))
  negDivAlg a b =
  (if 0  a + bb  0 then (-1, a + b) else adjust b (negDivAlg a (2 * b)))
  even Int.Pls = True
  even Int.Min = False
  even (Int.Bit0 x) = True
  even (Int.Bit1 x) = False
  even (number_of w) = even w
  number_of v mod number_of v' =
  (if neg (number_of v) then 0
   else if neg (number_of v') then number_of v
        else nat (number_of v mod number_of v'))
  number_of v div number_of v' =
  (if neg (number_of v) then 0 else nat (number_of v div number_of v'))
  nat (number_of w) = number_of w
  neg (number_of x) = neg x
  sorted_spvec [] = True
  sorted_spvec (a # as) =
  (case as of [] => True | b # bs => fst a < fst b ∧ sorted_spvec as)
  sorted_spmat [] = True
  sorted_spmat (a # as) = (sorted_spvec (snd a) ∧ sorted_spmat as)
  sorted_sparse_matrix A == sorted_spvec A ∧ sorted_spmat A
  mult_spmat [] A = []
  mult_spmat (a # as) A =
  (fst a, mult_spvec_spmat ([], snd a, A)) # mult_spmat as A
  mult_spvec_spmat (c, [], brr) = c
  mult_spvec_spmat (c, y # z, []) = c
  mult_spvec_spmat (c, a # arr, b # brr) =
  (if fst a < fst b then mult_spvec_spmat (c, arr, b # brr)
   else if fst b < fst a then mult_spvec_spmat (c, a # arr, brr)
        else mult_spvec_spmat (addmult_spvec (snd a, c, snd b), arr, brr))
  addmult_spvec (y, arr, []) = arr
  addmult_spvec (y, [], ae # af) = smult_spvec y (ae # af)
  addmult_spvec (y, a # arr, b # brr) =
  (if fst a < fst b then a # addmult_spvec (y, arr, b # brr)
   else if fst b < fst a then (fst b, y * snd b) # addmult_spvec (y, a # arr, brr)
        else (fst a, snd a + y * snd b) # addmult_spvec (y, arr, brr))
  smult_spvec y [] = []
  smult_spvec y (a # arr) = (fst a, y * snd a) # smult_spvec y arr
  add_spmat ([], bs) = bs
  add_spmat (w # x, []) = w # x
  add_spmat (a # as, b # bs) =
  (if fst a < fst b then a # add_spmat (as, b # bs)
   else if fst b < fst a then b # add_spmat (a # as, bs)
        else (fst a, add_spvec (snd a, snd b)) # add_spmat (as, bs))
  add_spvec (arr, []) = arr
  add_spvec ([], ab # ac) = ab # ac
  add_spvec (a # arr, b # brr) =
  (if fst a < fst b then a # add_spvec (arr, b # brr)
   else if fst b < fst a then b # add_spvec (a # arr, brr)
        else (fst a, snd a + snd b) # add_spvec (arr, brr))
  minus_spmat [] = []
  minus_spmat (a # as) = (fst a, minus_spvec (snd a)) # minus_spmat as
  minus_spvec [] = []
  minus_spvec (a # as) = (fst a, - snd a) # minus_spvec as
  abs_spmat [] = []
  abs_spmat (a # as) = (fst a, abs_spvec (snd a)) # abs_spmat as
  abs_spvec [] = []
  abs_spvec (a # as) = (fst a, ¦snd a¦) # abs_spvec as
  diff_spmat A B == add_spmat (A, minus_spmat B)
  le_spmat ([], []) = True
  le_spmat (a # as, []) = (le_spvec (snd a, []) ∧ le_spmat (as, []))
  le_spmat ([], b # bs) = (le_spvec ([], snd b) ∧ le_spmat ([], bs))
  le_spmat (a # as, b # bs) =
  (if fst a < fst b then le_spvec (snd a, []) ∧ le_spmat (as, b # bs)
   else if fst b < fst a then le_spvec ([], snd b) ∧ le_spmat (a # as, bs)
        else le_spvec (snd a, snd b) ∧ le_spmat (as, bs))
  le_spvec ([], []) = True
  le_spvec (a # as, []) = (snd a  (0::'a) ∧ le_spvec (as, []))
  le_spvec ([], b # bs) = ((0::'a)  snd b ∧ le_spvec ([], bs))
  le_spvec (a # as, b # bs) =
  (if fst a < fst b then snd a  (0::'a) ∧ le_spvec (as, b # bs)
   else if fst b < fst a then (0::'a)  snd b ∧ le_spvec (a # as, bs)
        else snd a  snd b ∧ le_spvec (as, bs))
  pprt_spmat [] = []
  pprt_spmat (a # as) = (fst a, pprt_spvec (snd a)) # pprt_spmat as
  pprt_spvec [] = []
  pprt_spvec (a # as) = (fst a, pprt (snd a)) # pprt_spvec as
  nprt_spmat [] = []
  nprt_spmat (a # as) = (fst a, nprt_spvec (snd a)) # nprt_spmat as
  nprt_spvec [] = []
  nprt_spvec (a # as) = (fst a, nprt (snd a)) # nprt_spvec as
  mult_est_spmat r1.0 r2.0 s1.0 s2.0 ==
  add_spmat
   (mult_spmat (pprt_spmat s2.0) (pprt_spmat r2.0),
    add_spmat
     (mult_spmat (pprt_spmat s1.0) (nprt_spmat r2.0),
      add_spmat
       (mult_spmat (nprt_spmat s2.0) (pprt_spmat r1.0),
        mult_spmat (nprt_spmat s1.0) (nprt_spmat r1.0))))
  map f [] = []
  map f (x # xs) = f x # map f xs
  split f (a, b) = f a b
  example_one ==
  [(0, [(0, 5), (2, - 9), (6, 8)]), (1, [(1, 4), (2, 0), (8, 8)]),
   (2, [(2, 6), (3, 7), (9, 23)]), (16, [(13, -8), (19, 12)]),
   (29, [(3, 5), (4, 6), (7, 8)])]
  example_one' ==
  [(0, [(0, 0), (2, 0), (6, 0)]), (1, [(1, 0), (2, 0), (8, 0)]),
   (2, [(2, 0), (3, 0), (9, 0)]), (29, [(3, 0), (4, 0), (7, 0)])]
  example_zero == [(3, [(3, 5)])]

Code generation formt the series @{term "Ψ::'a matrix => 'a::lordered_ring matrix"}

lemma beta_equiv:

  β = (λA. - (h (δ A)))

lemma sorted_spvec_beta_spmat:

  sorted_spvec A ==> sorted_spvec (β_spmat A)

lemma sorted_spvec_beta_spmat_nth:

  sorted_spvec A ==> sorted_spvec ((β_spmat ^ n) A)

lemma sorted_spmat_beta_spmat:

  sorted_spmat (β_spmat A)

lemma sorted_spmat_beta_spmat_nth:

  sorted_spmat A ==> sorted_spmat ((β_spmat ^ n) A)

lemma sorted_sparse_matrix_beta:

  sorted_sparse_matrix A ==> sorted_sparse_matrix (β_spmat A)

lemma sorted_sparse_matrix_beta_nth:

  sorted_sparse_matrix A ==> sorted_sparse_matrix ((β_spmat ^ n) A)

lemma beta_execute_sparse:

  β (sparse_row_matrix A) = sparse_row_matrix (β_spmat A)

corollary beta_nth_power_execute_sparse:

  (β ^ n) (sparse_row_matrix A) = sparse_row_matrix ((β_spmat ^ n) A)

lemma fin_sum_beta_up_to_n_execute_sparse:

  fin_sum β n (sparse_row_matrix A) =
  sparse_row_matrix (fin_sum_spmat β_spmat A n)

lemma local_bound_spmat_beta:

  local_bounded_func β
  ==> local_bound β (sparse_row_matrix A) =
      (LEAST n. sparse_row_matrix ((β_spmat ^ n) A) = 0)

lemma LEAST_execute_sparse_beta:

  sorted_sparse_matrix A
  ==> (LEAST n. sparse_row_matrix ((β_spmat ^ n) A) = 0) =
      (LEAST n. non_zero_spmat ((β_spmat ^ n) A) = 0)

lemma local_bound_beta_spmat:

  [| local_bounded_func β; sorted_sparse_matrix A |]
  ==> local_bound β (sparse_row_matrix A) =
      (LEAST n. non_zero_spmat ((β_spmat ^ n) A) = 0)

lemma local_bounded_func_beta_impl_terminates_loop:

  local_bounded_func β = (∀x. terminatesy. y  0, β, x))

lemma local_bounded_func_beta_impl_terminates_loop_spmat:

  sorted_sparse_matrix A
  ==> terminatesy. y  0, β, sparse_row_matrix A) =
      terminatesy. non_zero_spmat y  0, β_spmat, A)

lemma local_bounded_func_impl_terminates_beta_spmat_loop:

  [| sorted_sparse_matrix A; local_bounded_func β |]
  ==> terminatesy. non_zero_spmat y  0, β_spmat, A)

lemma LEAST_local_bound_beta_non_zero_spmat:

  non_zero_spmat A = 0 ==> (LEAST n. non_zero_spmat ((β_spmat ^ n) A) = 0) = 0

lemma local_bound_gen_spmat_beta_correct:

  terminatesy. non_zero_spmat y  0, β_spmat, A)
  ==> local_bound_gen_spmat β_spmat A m =
      m + (LEAST n. non_zero_spmat ((β_spmat ^ n) A) = 0)

lemma local_bound_beta_spmat_correct:

  terminatesy. non_zero_spmat y  0, β_spmat, A)
  ==> local_bound_spmat β_spmat A =
      (LEAST n. non_zero_spmat ((β_spmat ^ n) A) = 0)

lemma nrows_hom_oper_pert_decre:

  A  0 ==> nrows ((- (hom_oper_matrix1 o pert_matrix1)) A) < nrows A

corollary nrows_beta_decre:

  A  0 ==> nrows (β A) < nrows A

lemma local_bounded_func_β_matrix:

  local_bounded_func β

lemma local_bounded_func_beta_impl_local_bound_spmat_is_Least:

  sorted_sparse_matrix A
  ==> local_bound_spmat β_spmat A =
      (LEAST n. non_zero_spmat ((β_spmat ^ n) A) = 0)

lemma local_bound_eq_local_bound_spmat_beta:

  sorted_sparse_matrix A
  ==> local_bound_spmat β_spmat A = local_bound β (sparse_row_matrix A)

lemma psi_execute_sparse:

  sorted_sparse_matrix A
  ==> sparse_row_matrix (Ψ_spmat A) = Ψ (sparse_row_matrix A)

lemma compute_phi_psi_sparse:

  sorted_sparse_matrix A1
  ==> Φ (sparse_row_matrix A1) = sparse_row_matrix (Φ_spmat A1)
  Φ_spmat A = fin_sum_spmat α_spmat A (local_bound_spmat α_spmat A)
  sorted_sparse_matrix A1
  ==> Ψ (sparse_row_matrix A1) = sparse_row_matrix (Ψ_spmat A1)
  Ψ_spmat A == fin_sum_spmat β_spmat A (local_bound_spmat β_spmat A)
  sorted_sparse_matrix A1
  ==> local_bound α (sparse_row_matrix A1) = local_bound_spmat α_spmat A1
  sorted_sparse_matrix A1
  ==> local_bound β (sparse_row_matrix A1) = local_bound_spmat β_spmat A1
  local_bound_spmat f A == local_bound_gen_spmat f A 0
  local_bound_gen_spmat f A n = For (λy. non_zero_spmat y  0) fy n. n + 1) A n
  For continue f Ac x ac =
  (if continue x then For continue f Ac (f x) (Ac x ac) else ac)
  non_zero_spmat [] = 0
  non_zero_spmat (a # b) = non_zero_spvec (snd a) + non_zero_spmat b
  non_zero_spvec [] = 0
  non_zero_spvec (a # b) =
  (if snd a  (0::'a) then Suc (non_zero_spvec b) else non_zero_spvec b)
  terminates (continue, f, x) =
  (if continue x then terminates (continue, f, f x) else True)
  fin_sum_spmat f A n =
  (if n = 0 then A else add_spmat ((f ^ n) A, fin_sum_spmat f A (n - 1)))
  fin_sum_spmat f A (number_of n) =
  (if number_of n = 0 then A
   else add_spmat ((f ^ number_of n) A, fin_sum_spmat f A (number_of n - 1)))
  add_spmat ([], bs) = bs
  add_spmat (w # x, []) = w # x
  add_spmat (a # as, b # bs) =
  (if fst a < fst b then a # add_spmat (as, b # bs)
   else if fst b < fst a then b # add_spmat (a # as, bs)
        else (fst a, add_spvec (snd a, snd b)) # add_spmat (as, bs))
  f ^ number_of w = (if number_of w = 0 then id else f o f ^ (number_of w - 1))
  (f o g) x = f (g x)
  id x = x
  (α ^ n) (sparse_row_matrix A) = sparse_row_matrix ((α_spmat ^ n) A)
  (β ^ n) (sparse_row_matrix A) = sparse_row_matrix ((β_spmat ^ n) A)
  α (sparse_row_matrix A) = sparse_row_matrix (α_spmat A)
  α_spmat A = minus_spmat (pert_spmat_sort (hom_oper_spmat A))
  β (sparse_row_matrix A) = sparse_row_matrix (β_spmat A)
  β_spmat A = minus_spmat (hom_oper_spmat (pert_spmat_sort A))
  diff_matrix1 (sparse_row_matrix A) = sparse_row_matrix (differ_spmat' A)
  sparse_row_matrix (differ_spmat' A1) = sparse_row_matrix (differ_spmat A1)
  sparse_row_matrix (differ_spmat A) = sparse_row_matrix (differ_spmat_sort A)
  differ_spmat_sort [] = []
  differ_spmat_sort ((i, v) # vs) =
  (let m = sort_spvec
            (map (λ(j, x).
                     if j = 0 then (j, 0::'a)
                     else if (i + j) mod 2 = 0 then (j - 1, 0::'a)
                          else (j - 1, x))
              v);
       M = differ_spmat_sort vs
   in add_spmat ([(i, m)], M))
  hom_oper_matrix1 (sparse_row_matrix A) = sparse_row_matrix (hom_oper_spmat A)
  hom_oper_spmat [] = []
  hom_oper_spmat ((i, v) # vs) =
  (let m = map (λ(j, x). if (i + j) mod 2 = 1 then (j + 1, 0::'a) else (j + 1, x))
            v;
       M = hom_oper_spmat vs
   in add_spmat ([(i, m)], M))
  pert_matrix1 (sparse_row_matrix A) = sparse_row_matrix (pert_spmat_sort A)
  pert_spmat_sort [] = []
  pert_spmat_sort ((i, v) # vs) =
  (let m = sort_spvec
            (map (λ(j, x).
                     if i = 0 then (j, 0::'a)
                     else if (i + j) mod 2 = 0 then (j, 0::'a) else (j, x))
              v);
       M = pert_spmat_sort vs
   in add_spmat ([(i - 1, m)], M))
  minus_spmat [] = []
  minus_spmat (a # as) = (fst a, minus_spvec (snd a)) # minus_spmat as
  sort_spvec [] = []
  sort_spvec (a # b) = insert_sorted a (sort_spvec b)
  insert_sorted a [] = [a]
  insert_sorted a (b # c) =
  (if fst a < fst b then a # b # c
   else if fst a = fst b then (fst a, snd a + snd b) # c
        else b # insert_sorted a c)
  If True = (λx y. x)
  If False = (λx y. y)
  (¬ True) = False
  (¬ False) = True
  (P ∧ True) = P
  (True ∧ P) = P
  (P ∧ False) = False
  (False ∧ P) = False
  (P ∨ True) = True
  (True ∨ P) = True
  (P ∨ False) = P
  (False ∨ P) = P
  (True --> P) = P
  (P --> True) = True
  (True --> P) = P
  (P --> False) = (¬ P)
  (False --> P) = True
  (False = False) = True
  (True = True) = True
  (False = True) = False
  (True = False) = False
  fst (x, y) = x
  snd (x, y) = y
  ((a, b) = (c, d)) = (a = cb = d)
  prod_case f (x, y) = f x y
  the (Some x) = x
  (None = Some x) = False
  (Some x = None) = False
  (None = None) = True
  (Some x = Some y) = (x = y)
  option_case = (λa f opt. option_case_compute opt a f)
  option_case_compute None = (λa f. a)
  option_case_compute (Some x) = (λa f. f x)
  list_case = (λa f l. list_case_compute l a f)
  list_case_compute [] = (λa f. a)
  list_case_compute (u # v) = (λa f. f u v)
  length [] = 0
  length (x # xs) = 1 + length xs
  (x # xs) ! n = (if n = 0 then x else xs ! (n - 1))
  Let s f == f s
  If True = (λx y. x)
  If False = (λx y. y)
  Let s f == f s
  fst (x, y) = x
  snd (x, y) = y
  ((a, b) = (c, d)) = (a = cb = d)
  prod_case f (x, y) = f x y
  (¬ True) = False
  (¬ False) = True
  (P ∧ True) = P
  (True ∧ P) = P
  (P ∧ False) = False
  (False ∧ P) = False
  (P ∨ True) = True
  (True ∨ P) = True
  (P ∨ False) = P
  (False ∨ P) = P
  (True --> P) = P
  (P --> True) = True
  (True --> P) = P
  (P --> False) = (¬ P)
  (False --> P) = True
  (False = False) = True
  (True = True) = True
  (False = True) = False
  (True = False) = False
  Int.Bit0 Int.Pls = Int.Pls
  Int.Bit1 Int.Min = Int.Min
  iszero Int.Pls = True
  iszero Int.Min = False
  iszero (Int.Bit0 x) = iszero x
  iszero (Int.Bit1 x) = False
  neg Int.Pls = False
  neg Int.Min = True
  neg (Int.Bit0 x) = neg x
  neg (Int.Bit1 x) = neg x
  lezero Int.Pls = True
  lezero Int.Min = True
  lezero (Int.Bit0 x) = lezero x
  lezero (Int.Bit1 x) = neg x
  (Int.Pls = Int.Pls) = True
  (Int.Min = Int.Min) = True
  (Int.Pls = Int.Min) = False
  (Int.Min = Int.Pls) = False
  (Int.Bit0 x = Int.Bit0 y) = (x = y)
  (Int.Bit1 x = Int.Bit1 y) = (x = y)
  (Int.Bit0 x = Int.Bit1 y) = False
  (Int.Bit1 x = Int.Bit0 y) = False
  (Int.Pls = Int.Bit0 x) = (Int.Pls = x)
  (Int.Pls = Int.Bit1 x) = False
  (Int.Min = Int.Bit0 x) = False
  (Int.Min = Int.Bit1 x) = (Int.Min = x)
  (Int.Bit0 x = Int.Pls) = (x = Int.Pls)
  (Int.Bit1 x = Int.Pls) = False
  (Int.Bit0 x = Int.Min) = False
  (Int.Bit1 x = Int.Min) = (x = Int.Min)
  (Int.Pls < Int.Min) = False
  (Int.Pls < Int.Pls) = False
  (Int.Min < Int.Pls) = True
  (Int.Min < Int.Min) = False
  (Int.Bit0 x < Int.Bit0 y) = (x < y)
  (Int.Bit1 x < Int.Bit1 y) = (x < y)
  (Int.Bit0 x < Int.Bit1 y) = (x  y)
  (Int.Bit1 x < Int.Bit0 y) = (x < y)
  (Int.Pls < Int.Bit0 x) = (Int.Pls < x)
  (Int.Pls < Int.Bit1 x) = (Int.Pls  x)
  (Int.Min < Int.Bit0 x) = (Int.Pls  x)
  (Int.Min < Int.Bit1 x) = (Int.Min < x)
  (Int.Bit0 x < Int.Pls) = (x < Int.Pls)
  (Int.Bit1 x < Int.Pls) = (x < Int.Pls)
  (Int.Bit0 x < Int.Min) = (x < Int.Pls)
  (Int.Bit1 x < Int.Min) = (x < Int.Min)
  (Int.Pls  Int.Min) = False
  (Int.Pls  Int.Pls) = True
  (Int.Min  Int.Pls) = True
  (Int.Min  Int.Min) = True
  (Int.Bit0 x  Int.Bit0 y) = (x  y)
  (Int.Bit1 x  Int.Bit1 y) = (x  y)
  (Int.Bit0 x  Int.Bit1 y) = (x  y)
  (Int.Bit1 x  Int.Bit0 y) = (x < y)
  (Int.Pls  Int.Bit0 x) = (Int.Pls  x)
  (Int.Pls  Int.Bit1 x) = (Int.Pls  x)
  (Int.Min  Int.Bit0 x) = (Int.Pls  x)
  (Int.Min  Int.Bit1 x) = (Int.Min  x)
  (Int.Bit0 x  Int.Pls) = (x  Int.Pls)
  (Int.Bit1 x  Int.Pls) = (x < Int.Pls)
  (Int.Bit0 x  Int.Min) = (x < Int.Pls)
  (Int.Bit1 x  Int.Min) = (x  Int.Min)
  Int.succ Int.Pls = Int.Bit1 Int.Pls
  Int.succ Int.Min = Int.Pls
  Int.succ (Int.Bit0 k) = Int.Bit1 k
  Int.succ (Int.Bit1 k) = Int.Bit0 (Int.succ k)
  Int.pred Int.Pls = Int.Min
  Int.pred Int.Min = Int.Bit0 Int.Min
  Int.pred (Int.Bit0 k) = Int.Bit1 (Int.pred k)
  Int.pred (Int.Bit1 k) = Int.Bit0 k
  - Int.Pls = Int.Pls
  - Int.Min = Int.Bit1 Int.Pls
  - Int.Bit0 k = Int.Bit0 (- k)
  - Int.Bit1 k = Int.Bit1 (Int.pred (- k))
  Int.Pls + k = k
  Int.Min + k = Int.pred k
  k + Int.Pls = k
  k + Int.Min = Int.pred k
  Int.Bit0 k + Int.Bit0 l = Int.Bit0 (k + l)
  Int.Bit0 k + Int.Bit1 l = Int.Bit1 (k + l)
  Int.Bit1 k + Int.Bit0 l = Int.Bit1 (k + l)
  Int.Bit1 k + Int.Bit1 l = Int.Bit0 (k + Int.succ l)
  Int.Pls * w = Int.Pls
  Int.Min * k = - k
  x * Int.Pls = Int.Pls
  x * Int.Min = - x
  Int.Bit0 x * y = Int.Bit0 (x * y)
  x * Int.Bit0 y = Int.Bit0 (x * y)
  Int.Bit1 x * Int.Bit1 y = Int.Bit1 (Int.Bit0 (x * y) + x + y)
  0 = Numeral0
  1 = Numeral1
  nat_norm_number_of (number_of w) = (if lezero w then 0 else number_of w)
  Suc (number_of x) = (if neg x then 1 else number_of (Int.succ x))
  number_of x + number_of y =
  (if neg x then number_of y
   else if neg y then number_of x else number_of (x + y))
  number_of x - number_of y =
  (if neg x then 0
   else if neg y then number_of x else nat_norm_number_of (number_of (x + - y)))
  number_of x * number_of y =
  (if neg x then 0 else if neg y then 0 else number_of (x * y))
  (number_of x = number_of y) = (lezero x ∧ lezero yx = y)
  (number_of x < number_of y) = (x < y ∧ ¬ lezero y)
  (number_of x  number_of y) = (y < x --> lezero x)
  natfac n = (if n = 0 then 1 else n * natfac (n - 1))
  - number_of w = number_of (- w)
  number_of v + number_of w = number_of (v + w)
  number_of x - number_of y = number_of (x + - y)
  number_of v * number_of w = number_of (v * w)
  (0::'a) = Numeral0
  (1::'a) = Numeral1
  (number_of x = number_of y) = (x = y)
  (number_of x  number_of y) = (x  y)
  (number_of x < number_of y) = (x < y)
  max a b = (if a  b then b else a)
  min a b = (if a  b then a else b)
  real (number_of v) = (if neg v then 0 else number_of v)
  nat (number_of v) = number_of v
  real (number_of v) = number_of v
  z ^ number_of (Int.Bit0 w) = (let w = z ^ number_of w in w * w)
  z ^ number_of (Int.Bit1 w) =
  (if Numeral0  number_of w then let w = z ^ number_of w in z * w * w
   else Numeral1)
  z ^ Numeral0 = Numeral1
  z ^ -1 = Numeral1
  a div b = fst (divAlg (a, b))
  a mod b = snd (divAlg (a, b))
  divAlg (a, b) =
  (if 0  a
   then if 0  b then posDivAlg a b
        else if a = 0 then 0N else negateSnd (negDivAlg (- a) (- b))
   else if 0 < b then negDivAlg a b else negateSnd (posDivAlg (- a) (- b)))
  adjust b (q, r) = (if 0  r - b then (2 * q + 1, r - b) else (2 * q, r))
  negateSnd (q, r) = (q, - r)
  posDivAlg a b =
  (if a < bb  0 then (0, a) else adjust b (posDivAlg a (2 * b)))
  negDivAlg a b =
  (if 0  a + bb  0 then (-1, a + b) else adjust b (negDivAlg a (2 * b)))
  even Int.Pls = True
  even Int.Min = False
  even (Int.Bit0 x) = True
  even (Int.Bit1 x) = False
  even (number_of w) = even w
  number_of v mod number_of v' =
  (if neg (number_of v) then 0
   else if neg (number_of v') then number_of v
        else nat (number_of v mod number_of v'))
  number_of v div number_of v' =
  (if neg (number_of v) then 0 else nat (number_of v div number_of v'))
  nat (number_of w) = number_of w
  neg (number_of x) = neg x
  sorted_spvec [] = True
  sorted_spvec (a # as) =
  (case as of [] => True | b # bs => fst a < fst b ∧ sorted_spvec as)
  sorted_spmat [] = True
  sorted_spmat (a # as) = (sorted_spvec (snd a) ∧ sorted_spmat as)
  sorted_sparse_matrix A == sorted_spvec A ∧ sorted_spmat A
  mult_spmat [] A = []
  mult_spmat (a # as) A =
  (fst a, mult_spvec_spmat ([], snd a, A)) # mult_spmat as A
  mult_spvec_spmat (c, [], brr) = c
  mult_spvec_spmat (c, y # z, []) = c
  mult_spvec_spmat (c, a # arr, b # brr) =
  (if fst a < fst b then mult_spvec_spmat (c, arr, b # brr)
   else if fst b < fst a then mult_spvec_spmat (c, a # arr, brr)
        else mult_spvec_spmat (addmult_spvec (snd a, c, snd b), arr, brr))
  addmult_spvec (y, arr, []) = arr
  addmult_spvec (y, [], ae # af) = smult_spvec y (ae # af)
  addmult_spvec (y, a # arr, b # brr) =
  (if fst a < fst b then a # addmult_spvec (y, arr, b # brr)
   else if fst b < fst a then (fst b, y * snd b) # addmult_spvec (y, a # arr, brr)
        else (fst a, snd a + y * snd b) # addmult_spvec (y, arr, brr))
  smult_spvec y [] = []
  smult_spvec y (a # arr) = (fst a, y * snd a) # smult_spvec y arr
  add_spmat ([], bs) = bs
  add_spmat (w # x, []) = w # x
  add_spmat (a # as, b # bs) =
  (if fst a < fst b then a # add_spmat (as, b # bs)
   else if fst b < fst a then b # add_spmat (a # as, bs)
        else (fst a, add_spvec (snd a, snd b)) # add_spmat (as, bs))
  add_spvec (arr, []) = arr
  add_spvec ([], ab # ac) = ab # ac
  add_spvec (a # arr, b # brr) =
  (if fst a < fst b then a # add_spvec (arr, b # brr)
   else if fst b < fst a then b # add_spvec (a # arr, brr)
        else (fst a, snd a + snd b) # add_spvec (arr, brr))
  minus_spmat [] = []
  minus_spmat (a # as) = (fst a, minus_spvec (snd a)) # minus_spmat as
  minus_spvec [] = []
  minus_spvec (a # as) = (fst a, - snd a) # minus_spvec as
  abs_spmat [] = []
  abs_spmat (a # as) = (fst a, abs_spvec (snd a)) # abs_spmat as
  abs_spvec [] = []
  abs_spvec (a # as) = (fst a, ¦snd a¦) # abs_spvec as
  diff_spmat A B == add_spmat (A, minus_spmat B)
  le_spmat ([], []) = True
  le_spmat (a # as, []) = (le_spvec (snd a, []) ∧ le_spmat (as, []))
  le_spmat ([], b # bs) = (le_spvec ([], snd b) ∧ le_spmat ([], bs))
  le_spmat (a # as, b # bs) =
  (if fst a < fst b then le_spvec (snd a, []) ∧ le_spmat (as, b # bs)
   else if fst b < fst a then le_spvec ([], snd b) ∧ le_spmat (a # as, bs)
        else le_spvec (snd a, snd b) ∧ le_spmat (as, bs))
  le_spvec ([], []) = True
  le_spvec (a # as, []) = (snd a  (0::'a) ∧ le_spvec (as, []))
  le_spvec ([], b # bs) = ((0::'a)  snd b ∧ le_spvec ([], bs))
  le_spvec (a # as, b # bs) =
  (if fst a < fst b then snd a  (0::'a) ∧ le_spvec (as, b # bs)
   else if fst b < fst a then (0::'a)  snd b ∧ le_spvec (a # as, bs)
        else snd a  snd b ∧ le_spvec (as, bs))
  pprt_spmat [] = []
  pprt_spmat (a # as) = (fst a, pprt_spvec (snd a)) # pprt_spmat as
  pprt_spvec [] = []
  pprt_spvec (a # as) = (fst a, pprt (snd a)) # pprt_spvec as
  nprt_spmat [] = []
  nprt_spmat (a # as) = (fst a, nprt_spvec (snd a)) # nprt_spmat as
  nprt_spvec [] = []
  nprt_spvec (a # as) = (fst a, nprt (snd a)) # nprt_spvec as
  mult_est_spmat r1.0 r2.0 s1.0 s2.0 ==
  add_spmat
   (mult_spmat (pprt_spmat s2.0) (pprt_spmat r2.0),
    add_spmat
     (mult_spmat (pprt_spmat s1.0) (nprt_spmat r2.0),
      add_spmat
       (mult_spmat (nprt_spmat s2.0) (pprt_spmat r1.0),
        mult_spmat (nprt_spmat s1.0) (nprt_spmat r1.0))))
  map f [] = []
  map f (x # xs) = f x # map f xs
  split f (a, b) = f a b
  example_one ==
  [(0, [(0, 5), (2, - 9), (6, 8)]), (1, [(1, 4), (2, 0), (8, 8)]),
   (2, [(2, 6), (3, 7), (9, 23)]), (16, [(13, -8), (19, 12)]),
   (29, [(3, 5), (4, 6), (7, 8)])]
  example_one' ==
  [(0, [(0, 0), (2, 0), (6, 0)]), (1, [(1, 0), (2, 0), (8, 0)]),
   (2, [(2, 0), (3, 0), (9, 0)]), (29, [(3, 0), (4, 0), (7, 0)])]
  example_zero == [(3, [(3, 5)])]

lemma funpow_equiv:

  (g o f) ^ Suc n = g o (f o g) ^ n o f

lemma f_id:

  f o id = f

lemma fun_Compl_equiv:

  - (f o g) = - f o g

lemma funpow_zip_next:

  f ^ (n + 1) = f ^ n o f

lemma

  0 = sparse_row_vector []

lemma

  sparse_row_matrix A + sparse_row_matrix B = sparse_row_matrix (add_spmat (A, B))

lemma

  sparse_row_vector a + sparse_row_vector b = sparse_row_vector (add_spvec (a, b))

lemma

  pert_matrix1 (sparse_row_matrix A) = sparse_row_matrix (pert_spmat_sort A)
  pert_spmat_sort [] = []
  pert_spmat_sort ((i, v) # vs) =
  (let m = sort_spvec
            (map (λ(j, x).
                     if i = 0 then (j, 0::'a)
                     else if (i + j) mod 2 = 0 then (j, 0::'a) else (j, x))
              v);
       M = pert_spmat_sort vs
   in add_spmat ([(i - 1, m)], M))

lemma

  hom_oper_matrix1 (sparse_row_matrix A) = sparse_row_matrix (hom_oper_spmat A)
  hom_oper_spmat [] = []
  hom_oper_spmat ((i, v) # vs) =
  (let m = map (λ(j, x). if (i + j) mod 2 = 1 then (j + 1, 0::'a) else (j + 1, x))
            v;
       M = hom_oper_spmat vs
   in add_spmat ([(i, m)], M))

lemma

  - A = (λx. - A x)

lemma

  α = α

lemma

  α = alpha_matrix

lemma

  alpha_matrix (sparse_row_matrix A) = sparse_row_matrix (α_spmat A)

lemma

  α_spmat A = minus_spmat (pert_spmat_sort (hom_oper_spmat A))