Theory Van_der_Waerden

theory Van_der_Waerden
  imports Main "HOL-Library.FuncSet" Digits
begin

section ‹Van der Waerden's Theorem›

text ‹In combinatorics, Van der Waerden's Theorem is about arithmetic progressions of a certain
length of the same colour in a colouring of an interval. In order to state the theorem and to
prove it, we need to formally introduce arithmetic progressions. We will express $k$-colourings as
functions mapping an integer interval to the set $\{0,\dots , k-1 \}$ of colours.›

subsection ‹Arithmetic progressions›

text ‹A sequence of integer numbers with the same step size is called an arithmetic progression.
 We say an  $m$-fold arithmetic progression is an arithmetic progression with multiple step 
lengths.›

text ‹ Arithmetic progressions are defined in the following using the variables:

\begin{tabular}{lcp{8cm}}
$start$:& int›& starting value\\
$step$:&  nat›& positive integer for step length\\
$i$:&     nat›& $i$-th value in the arithmetic progression \\
\end{tabular}›

definition arith_prog :: "int  nat  nat  int"
  where "arith_prog start step i = start + int (i * step)"

text ‹ An $m$-fold arithmetic progression (which we will also call a multi-arithmetic progression)
is defined in the following using the variables:

\begin{tabular}{lcp{8cm}}
$dims$:&   nat›& number of dimensions/step directions of $m$-fold arithmetic progression\\
$start$:&  int›& starting value\\
$steps$:&  nat ⇒ nat›& function of steps, returns step in $i$-th dimension for $i\in[0..<dims]$\\
$c$:&      nat ⇒ nat›& function of coefficients, returns coefficient in $i$-th dimension for 
           $i\in[0..<dims]$ \\
\end{tabular}›

definition multi_arith_prog :: 
    "nat  int  (nat  nat)  (nat  nat)  int"
  where "multi_arith_prog dims start steps c = 
           start + int (i<dims. c i * steps i)"

text ‹An $m$-fold arithmetic progression of dimension $1$ is also an arithmetic progression and 
  vice versa. This is shown in the following lemmas.›
lemma multi_to_arith_prog: 
  "multi_arith_prog 1 start steps c = 
    arith_prog start (steps 0) (c 0)"
  unfolding multi_arith_prog_def arith_prog_def by auto

lemma arith_prog_to_multi: 
  "arith_prog start step c = 
    multi_arith_prog 1 start (λ_. step) (λ_. c)"
  unfolding multi_arith_prog_def arith_prog_def by auto

text ‹To show that an arithmetic progression is well-defined, we introduce the following predicate.
It assures that arith_prog start step ` [0..<l]› is contained in the integer interval $[a..b]$.›
definition is_arith_prog_on :: 
    "nat  int  nat  int  int  bool" 
  where "is_arith_prog_on l start step a b 
    (start  a  arith_prog start step (l-1)  b)"

text ‹Furthermore, we have monotonicity for arithmetic progressions.›
lemma arith_prog_mono: 
  assumes "c  c'"
  shows   "arith_prog start step c  arith_prog start step c'"
  using assms unfolding arith_prog_def by (auto intro: mult_mono)

text ‹Now, we state the well-definedness of an arithmetic progression of length $l$ in an integer
interval $[a..b]$. 
Indeed, is_arith_prog_on› guarantees that every element of arith_prog start step› of length $l$ 
  lies in $[a..b]$.›
lemma is_arith_prog_onD:
  assumes "is_arith_prog_on l start step a b"
  assumes "c  {0..<l}"
  shows   "arith_prog start step c  {a..b}"
proof -
  have "arith_prog start step 0  arith_prog start step c"
    by (rule arith_prog_mono) auto
  hence "arith_prog start step c  a"
    using assms by (simp add: arith_prog_def is_arith_prog_on_def 
                      add_increasing2)
  moreover have "arith_prog start step (l-1)  
                   arith_prog start step c"
    by (rule arith_prog_mono) (use assms(2) in auto)
  hence "arith_prog start step c  b"
    using assms unfolding arith_prog_def is_arith_prog_on_def 
    by linarith
  ultimately show ?thesis
    by auto
qed

text ‹We also need a predicate for an $m$-fold arithmetic progression to be well-defined. 
It assures that multi_arith_prog start step ` [0..<l]^m› is contained in $[a..b]$.›
definition is_multi_arith_prog_on :: 
    "nat  nat  int  (nat  nat)  int  int  bool" 
  where "is_multi_arith_prog_on l m start steps a b 
     (start  a  multi_arith_prog m start steps (λ_. l-1)  b)"

text ‹Moreover, we have monotonicity for $m$-fold arithmetic progressions as well.›
lemma multi_arith_prog_mono:
  assumes "i. i < m  c i  c' i"
  shows   "multi_arith_prog m start steps c  
            multi_arith_prog m start steps c'"
  using assms unfolding multi_arith_prog_def 
  by (auto intro!: sum_mono intro: mult_right_mono)

text ‹Finally, we get the well-definedness for $m$-fold arithmetic progressions of length $l$.
Here, is_multi_arith_prog_on› guarantees that every element of multi_arith_prog start step› 
  of length $l$ lies in $[a..b]$.›
lemma is_multi_arith_prog_onD:
  assumes "is_multi_arith_prog_on l m start steps a b"
  assumes "c  {0..<m}  {0..<l}"
  shows   "multi_arith_prog m start steps c  {a..b}"
proof -
  have "multi_arith_prog m start steps (λ_. 0)  
          multi_arith_prog m start steps c"
    by (rule multi_arith_prog_mono) auto
  hence "multi_arith_prog m start steps c  a"
    using assms by (simp add: multi_arith_prog_def 
       is_multi_arith_prog_on_def)
  moreover have "multi_arith_prog m start steps (λ_. l-1)  
                   multi_arith_prog m start steps c"
    by (rule multi_arith_prog_mono) (use assms in force)
  hence "multi_arith_prog m start steps c  b"
    using assms by (simp add: multi_arith_prog_def 
        is_multi_arith_prog_on_def)
  ultimately show ?thesis
    by auto
qed


subsection ‹Van der Waerden's Theorem›

text ‹The property for a number $n$ to fulfill Van der Waerden's theorem is the following:\\
For a $k$-colouring col of $[a..b]$ there exist
\begin{itemize}
\item $start$: starting value of an arithmetic progression
\item $step$:  step length of an arithmetic progression
\item $j$: colour 
\end{itemize}
such that arith_prog start step› is a valid arithmetic progression of length $l$ lying 
in $[a..b]$ of the same colour $j$.

The following variables will be used:\\
\begin{tabular}{lcp{8cm}}
$k$:& nat›& number of colours in segment colouring on $[a..b]$\\
$l$:& nat›& length of arithmetic progression\\
$n$:& nat›& number fulfilling Van der Waerden's Theorem\\
\end{tabular}
›
definition vdw :: 
    "nat  nat  nat  bool" 
  where "vdw k l n 
     (a b col. b + 1  a + int n  col  {a..b}  {..<k} 
       (j start step. j < k  step > 0  
        is_arith_prog_on l start step a b 
        arith_prog start step ` {..<l}  col -` {j}  {a..b}))"

text ‹To better work with the property of Van der Waerden's theorem, we introduce an 
  elimination rule.›
lemma vdwE:
  assumes "vdw k l n"
          "b + 1  a + int n" 
          "col  {a..b}  {..<k}"
  obtains j start step where
    "j < k" "step > 0" 
    "is_arith_prog_on l start step a b"
    "arith_prog start step ` {..<l}  col -` {j}  {a..b}"
  using assms that unfolding vdw_def by metis

text ‹Van der Waerden's theorem implies that the number fulfilling it is positive. This is show 
in the following lemma.›
lemma vdw_imp_pos:
  assumes "vdw k l n" 
          "l > 0"
  shows "n > 0"
proof (rule Nat.gr0I)
  assume [simp]: "n = 0"
  show False
    using assms 
    by (elim vdwE[where a = 1 and b = 0 and col = "λ_. 0"]) 
       (auto simp: lessThan_empty_iff)
qed

text ‹Van der Waerden's Theorem is trivial for a non-existent colouring. 
It also makes no sense for arithmetic progressions of length 0.›
lemma vdw_0_left [simp, intro]: "n>0  vdw 0 l n"
  by (auto simp: vdw_def)

text ‹In the case of $k=1$, Van der Waerden's Theorem holds. Then every number has the same colour,
hence also the arithmetic progression. A possible choice for the number fulfilling Van der 
Waerden Theorem is $l$.›
lemma vdw_1_left: 
  assumes "l>0" 
  shows "vdw 1 l l"
unfolding vdw_def
proof (safe, goal_cases)
  case (1 a b col)
  have "arith_prog a 1 ` {..<l}  {a..b}"
    using 1(1) by (auto simp: arith_prog_def)
  also have "{a..b} = col -` {0}  {a..b}"
    using 1(2) by auto
  finally have "arith_prog a 1 ` {..<l}  col -` {0}  {a..b}"
    by auto
  moreover have "is_arith_prog_on l a 1 a b" 
    unfolding is_arith_prog_on_def arith_prog_def using 1 assms 
    by auto
  ultimately show "j start step. j < 1  0 < step  
        is_arith_prog_on l start step a b 
        arith_prog start step ` {..<l}  col -` {j}  {a..b}"
    by auto
qed

text ‹In the case $l=1$, Van der Waerden's Theorem holds. As the length of the arithmetic 
progression is $1$, it consists of just one element. Thus every nonempty integer interval fulfills 
the Van der Waerden property. We can prove $N_{k,1}$ to be $1$.›
lemma vdw_1_right: "vdw k 1 1"
unfolding vdw_def 
proof safe
  fix a b :: int and col :: "int  nat"
  assume *: "a + int 1  b + 1" "col  {a..b}  {..<k}"
  have "col a < k" using * by auto
  have "arith_prog a 1 ` {..<1} = {a}"
    using *(1) by (auto simp: arith_prog_def)
  also have "{a}  col -` {col a}  {a..b}"
    using * by auto
  finally have "arith_prog a 1 ` {..<1}  col -` {col a}  {a..b}"
    by auto
  moreover have "is_arith_prog_on 1 a 1 a b" 
    unfolding is_arith_prog_on_def arith_prog_def
    using * by auto
  ultimately show  "j start step.
          j < k  0 < step  is_arith_prog_on 1 start step a b 
          arith_prog start step ` {..<1}  col -` {j}  {a..b}"
    using col a <k by blast
qed

text ‹In the case $l=2$, Van der Waerden's Theorem holds as well. Here, any two distinct numbers 
form an arithmetic progression of length $2$. Thus we only have to find two numbers with the same 
colour.
Using the pigeonhole principle on $k+1$ values, we can find two integers with the same colour.›
lemma vdw_2_right: "vdw k 2 (k+1)"
unfolding vdw_def 
proof safe
  fix a b :: int and col :: "int  nat"
  assume *: "a + int (k + 1)  b + 1" "col  {a..b}  {..<k}"

  have "col ` {a..b}  {..<k}" using *(2) by auto
  moreover have "k+1  card {a..b}" using *(1) by auto
  ultimately have "card (col ` {a..b}) < card {a..b}" using * 
    by (metis card_lessThan card_mono finite_lessThan le_less_trans 
        less_add_one not_le)
  then have "¬ inj_on col {a..b}" using pigeonhole[of col "{a..b}"]
    by auto
  then obtain start start_step 
    where pigeon: "col start = col start_step" 
      "start < start_step"
      "start  {a..b}" 
      "start_step  {a..b}" 
    using inj_onI[of "{a..b}" col] 
    by (metis not_less_iff_gr_or_eq)

  define step where "step = nat (start_step - start)"
  define j where "j = col start"

  have "j < k" unfolding j_def using *(2) pigeon(3) by auto 
  moreover have "0 < step" unfolding step_def using pigeon(2) by auto
  moreover have "is_arith_prog_on 2 start step a b" 
    unfolding is_arith_prog_on_def arith_prog_def step_def 
    using pigeon by auto
  moreover {
  have "arith_prog start step i  {start, start_step}" if "i<2" for i
    using that arith_prog_def step_def by (auto simp: less_2_cases_iff)
  also have "  col -` {j}  {a..b}" 
    using pigeon unfolding j_def by auto
  finally have "arith_prog start step ` {..<2}  col -` {j}  {a..b}" 
    by auto
  }
  ultimately show "j start step.
          j < k 
          0 < step 
          is_arith_prog_on 2 start step a b 
          arith_prog start step ` {..<2}  col -` {j}  {a..b}" by blast
qed

text ‹In order to prove Van der Waerden's Theorem, we first prove a slightly different lemma.
The statement goes as follows:\\
For a $k$-colouring $col$ on $[a..b]$ there exist
\begin{itemize}
\item  $start$: starting value of an arithmetic progression
\item  $steps$: step length of an arithmetic progression
\end{itemize}
such that f = multi_arith_prog m start step› is a valid $m$-fold arithmetic progression of 
length $l$ lying in $[a..b]$ such that for every $s<m$ have: if $c j < l$ for all $j\leq s$ then
$f(c_0, c_1, \dots, c_{m-1})$ and $f(0,\dots,0, c_{s+1},\dots, c_{m-1})$ have the same colour.

The property of the lemma uses the following variables:\\
\begin{tabular}{lcp{8cm}}
$k$:& nat›& number of colours in segment colouring of $[a..b]$\\
$m$:& nat›& dimension of $m$-fold arithmetic progression\\
$l$:& nat›& $l+1$ is length of $m$-fold arithmetic progression\\
$n$:& nat›& number fulfilling vdw_lemma›\\
\end{tabular}
›
definition vdw_lemma :: "nat  nat  nat  nat  bool" where
  "vdw_lemma k m l n 
     (a b col. b + 1  a + int n  col  {a..b}  {..<k} 
       (start steps. (i<m. steps i > 0)  
        is_multi_arith_prog_on (l+1) m start steps a b  (
           let f = multi_arith_prog m start steps
           in  (c  {0..<m}  {0..l}. s<m. ( j  s. c j < l) 
                  col (f c) = col (f (λi. if i  s then 0 else c i))))))"

text ‹To better work with this property, we introduce an elimination rule for vdw_lemma›.›
lemma vdw_lemmaE:
  fixes a b :: int
  assumes "vdw_lemma k m l n"
    "b + 1  a + int n" "col  {a..b}  {..<k}"
  obtains start steps where
    "i. i < m  steps i > 0"
    "is_multi_arith_prog_on (l+1) m start steps a b"
    "let f = multi_arith_prog m start steps
     in  c  {0..<m}  {0..l}. s<m. ( j  s. c j < l) 
            col (f c) = col (f (λi. if i  s then 0 else c i))"
  using assms that unfolding vdw_lemma_def by blast

text ‹To simplify the following proof, we show the following formula.›
lemma sum_mod_poly: 
  assumes "(k::nat)>0" 
  shows "(k - 1) * ( n{..<q}. k^n) < k^q "
proof -
  have "int ((k - 1) * (n<q. k ^ n)) = 
        (int k - 1) * (n<q. int k ^ n)"
    using assms by (simp add: of_nat_diff)
  also have " = int k ^ q - 1"
    by (induction q) (auto simp: algebra_simps)
  also have " < int (k ^ q)"
    by simp
  finally show ?thesis by linarith
qed

text ‹The proof of Van der Waerden's Theorem now proceeds in three steps:\\
\begin{itemize}
\item Firstly, we show that the vdw› property for all $k$ proves the vdw_lemma› for fixed $l$ but 
arbitrary $k$ and $m$. This is done by induction over $m$.
\item Secondly, we show that vdw_lemma› implies the induction step of vdw› using the pigeonhole 
principle.
\item Lastly, we combine the previous steps in an induction over $l$ to show Van der Waerden's 
Theorem in the general setting.
\end{itemize}›

text ‹Firstly, we need to show that vdw› for arbitrary $k$ implies vdw_lemma› for fixed $l$.
As mentioned earlier, we use induction over $m$.›
lemma vdw_imp_vdw_lemma:
  fixes l
  assumes vdw_assms: "k'. k'>0  n_k'. vdw k' l n_k'"
    and "l  2"
    and "m > 0"
    and "k > 0"
  shows   "N. vdw_lemma k m l N"
using m>0 k>0 proof (induction m rule: less_induct)
  case (less m)
  consider  "m=1" | "m>1" using less.prems by linarith
  then show ?case 
  proof cases
    text ‹ Case $m=1$: Show vdw_lemma› for arithmetic progression, Induction start. ›
    assume "m = 1"

    obtain n where vdw: "vdw k l n" using vdw_assms k>0 by blast
    define N where "N = 2*n"
    have "l>0" and "l>1" using l2 by auto

    have "vdw_lemma k m l N"
      unfolding vdw_lemma_def
    proof (safe, goal_cases)
      case (1 a b col)
      text ‹ Divide $[a..b]$ in two intervals $I_1$, $I_2$ of same length and obtain arithmetic 
        progression of length $l$ in $I_1$. ›
      have col_restr: "col  {a..a + int n - 1}  {..<k}"
        using 1 by (auto simp: N_def)
      then obtain j start step where prog:
        "j < k" "step > 0" 
        "is_arith_prog_on l start step a (a + int n -1)"
        "arith_prog start step ` {..<l}  
          col -` {j}  {a..a + int n - 1}"
        using vdw 1 unfolding N_def by (elim vdwE)(auto simp:is_arith_prog_on_def)
      have range_prog_lessThan_l: 
        "arith_prog start step i  {a..a + int n -1}" if "i < l" for i
        using that prog by auto

      have "{a..a + int n-1}{a..b}" using N_def "1"(1) by auto 
      then have "a + 2* int n - 1  b" using 1(1) unfolding N_def 
        by auto

      text ‹ Show that arith_prog start step› is an arithmetic progression of length $l+1$
         in $[a..b]$. ›
      have prog_in_ivl: "arith_prog start step i  {a..b}" 
        if "i  l" for i
      proof (cases "i=l")
        case False
        have "i<l" using that False by auto
        then show ?thesis 
          using range_prog_lessThan_l {a..a + int n-1}{a..b} by force
      next
        case True
        text ‹ Show $step›\leq |I_1|$ then have arith_prog start step (l+1)∈[a..b]› as 
           arith_prog start step (l+1) = arith_prog start step l + step›
        have "start  {a..a + int n -1}" 
          using range_prog_lessThan_l[of 0] 
          unfolding arith_prog_def by (simp add: 0 < l)
        moreover have "start + int step  {a..a + int n -1}" 
          using range_prog_lessThan_l[of 1] 
          unfolding arith_prog_def by (metis 1 < l mult.left_neutral)
        ultimately have "step  n" by auto
        have "arith_prog start step (l-1)  {a..a + int n -1}" 
          using range_prog_lessThan_l[of "l-1"] unfolding arith_prog_def
          using 0 < l diff_less less_numeral_extra(1) by blast
        moreover have "arith_prog start step l = 
                        arith_prog start step (l-1) + int step"
          unfolding arith_prog_def using 0 < l mult_eq_if by force
        ultimately have "arith_prog start step l  {a..b}" 
          using stepn N_def a + 2* int n -1  b by auto
        then show ?thesis using range_prog_lessThan_l using True 
          by force
      qed

      have col_prog_eq: "col (arith_prog start step k) = j" 
        if "k < l" for k
        using prog that by blast
      
      define steps :: "nat  nat" where steps_def: "steps = (λi. step)"
      define f where "f = multi_arith_prog 1 start steps"
      
      have rel_prop_1: 
        "col (f c) = col (f (λi. if i < s then 0 else c i))"
        if "c  {0..<1}  {0..l}" "s<1" "js. c j < l" for c s 
        using that by auto

      have arith_prog_on: 
        "is_multi_arith_prog_on (l+1) m start steps a b"
        using prog(3) unfolding is_arith_prog_on_def is_multi_arith_prog_on_def
        using m=1 arith_prog_to_multi steps_def prog_in_ivl by auto
      
      show ?case
        by (rule exI[of _ start], rule exI[of _ steps])
           (use rel_prop_1 step > 0 m = 1 arith_prog_on col_prog_eq
             multi_to_arith_prog in auto simp: f_def Let_def steps_def)
    qed
    then show ?case ..

  next
    text ‹ Case $m>1$: Show vdw_lemma› for $m$-fold arithmetic progression, 
          Induction step $(m-1) \longrightarrow m$. ›
    assume "m>1"

    obtain q where vdw_lemma_IH:"vdw_lemma k (m-1) l q" 
      using 1 < m less by force
    have "k^q>0" using k>0 by auto
    obtain n_kq where vdw: "vdw (k^q) l n_kq" 
      using vdw_assms k^q>0 by blast
    define N where "N = q + 2 * n_kq"

    text ‹Idea: $[a..b] = I_1 \cup I_2$ where $|I_1| = 2*n_{k,q}$ and $|I_2| = q$.
                Divide $I_1$ into blocks of length $q$ and define a new colouring on the set of 
                $q$-blocks where the colour of the block is the $k$-basis representation where 
                the $i$-th digit corresponds to the colour of the $i$-th element in the block. 
                Get an arithmetic progression of $q$-blocks of length $l+1$ in $I_1$, such that
                the first $l$ $q$-blocks have the same colour. 
                The step of the block-arithmetic progression is going to be the additional 
                step in the induction over $m$. ›

    have "vdw_lemma k m l N"
      unfolding vdw_lemma_def
    proof (safe, goal_cases)
      case (1 a b col)
      have "n_kq>0" using vdw_imp_pos vdw l2 by auto
      then have "N>0" by (simp add:N_def)
      then have "ab" using 1 by auto
      then have "k>0" using 1 by (intro Nat.gr0I) force
      have "l>0" and "l>1" using l2 by auto
      interpret digits k by (simp add: 0 < k digits_def)
      define col1 where "col1 = (λ x. from_digits q (λy. col (x + y)))" 
      have range_col1: "col1{a..a + int n_kq - 1}  {..<k^q}" 
      unfolding Pi_def
      proof safe
        fix x assume "x{a..a + int n_kq - 1}"
        then have col_xn:"col (x + int n){..<k}" if "n<q" for n :: nat
          using that 1 PiE N_def by auto
        have col_xn_upper_bound:"col (x + int n)  k - 1" 
          if "n<q" for n ::nat
          using that col_xn[of n] k>0 by (auto)
        have "(n<q. col (x + int n) * k ^ n) 
               (n<q. (k-1) *  k ^ n)"
          using col_xn_upper_bound by (intro sum_mono mult_right_mono) 
            auto
        also have " = (k-1) * (n<q. k ^ n)"
          by (rule sum_distrib_left[symmetric])
        also have " < k^q" using sum_mod_poly k>0 by auto        
        finally show "col1 x <k^q" unfolding col1_def from_digits_altdef 
          by auto 
      qed

      obtain j start step where prog:
        "j < k^q" "step > 0" 
        "is_arith_prog_on l start step a (a + int n_kq - 1)"
        "arith_prog start step ` {..<l}  
          col1 -` {j}  {a..a + int n_kq -1}"
        using vdw range_col1 by (elim vdwE) (auto simp: k>0) 

      have range_prog_lessThan_l: 
        "arith_prog start step i  {a..a + int n_kq -1}" 
        if "i < l" for i
        using that prog by auto

      have prog_in_ivl: 
        "arith_prog start step i  {a..a + 2 * int n_kq -1}" 
        if "i  l" for i
      proof (cases "i=l")
        case False
        then have "i<l" using that by auto
        then show ?thesis using prog by auto
      next
        case True
        have "start  {a..a + int n_kq -1}" 
          using range_prog_lessThan_l[of 0] unfolding arith_prog_def 
          by (simp add: 0 < l)
        moreover have "start + step  {a..a + int n_kq -1}" 
          using range_prog_lessThan_l[of 1] unfolding arith_prog_def 
          by (metis 1 < l mult.left_neutral)
        ultimately have "step  n_kq" by auto
        have "arith_prog start step (l-1)  {a..a + int n_kq -1}" 
          using range_prog_lessThan_l[of "l-1"] unfolding arith_prog_def
          using 0 < l diff_less less_numeral_extra(1) by blast
        moreover have "arith_prog start step l = 
            arith_prog start step (l-1) + step"
          unfolding arith_prog_def using 0 < l mult_eq_if by force
        ultimately have "arith_prog start step l  
            {a..a + 2 * int n_kq - 1}" 
          using stepn_kq by auto
        then show ?thesis using range_prog_lessThan_l using True 
          by force
      qed

      have col_prog_eq: "col1 (arith_prog start step k) = j" 
        if "k < l" for k
        using prog that by blast

      have digit_col1:"digit (col1 x) y = col (x+int y)" 
        if "x{a..<a + 2*int n_kq}" "y{..<q}" 
        for x::int and y::nat unfolding col1_def using that
      proof -
        have "j'. j'<q  x+j'{a..b}" 
          using "1"(1) N_def that(1) by force
        then have "j'. j'<q  (λy. col (x+int y)) j' < k" 
          using 1 that by auto
        then show "digit (from_digits q (λxa. col (x + int xa))) y = 
                    col (x + int y)" 
          using digit_from_digits that 1 by auto
      qed

      text ‹ Impact on the colour when taking the block-step. ›
      have one_step_more:
        "col (arith_prog start' step i) = digit j (nat (start'-start))" 
        if "start'{start..<start+q}" "i{..<l}" for start' i
      proof -
        have "start  start'" using that by simp
        have shift_arith_prog:
          "arith_prog start step i + (start' - start) = 
            arith_prog start' step i" 
          unfolding arith_prog_def by simp
        define diff where "diff = nat (start'-start)"
        have "diff {..<q}" using that unfolding diff_def by auto
        have "col (arith_prog start step i + int diff) = digit j diff"
        proof -
          have "col1 (arith_prog start step i) = j" 
            using col1_def prog that by blast
          moreover have " arith_prog start step i{a..a + 2 * int n_kq-1}"
            using prog(4) that by auto
          ultimately show ?thesis 
            using digit_col1[where x = "arith_prog start step i" 
                and y = "diff"] 
              prog 1 diff {..<q} by auto
        qed
        then show ?thesis unfolding diff_def 1
          by (auto simp: startstart' shift_arith_prog) 
      qed

      have one_step_more': "col (arith_prog start' step i) =
        col (arith_prog start' step 0)"
        if "start'{start..<start+q}" "i{..<l}" for start' i
        using that one_step_more[of start' 0] 
          one_step_more[of start' i] by auto

      have start_q: "start + int q  start + int q - 1 + 1" by linarith
      have "{start..start + int q-1}  {a..b}"
        using prog N_def 1(1) by (force simp: arith_prog_def is_arith_prog_on_def)  
      then have col': "col  {start..start + int q-1}  {..<k}"
        using 1 prog(4) by auto

      text ‹ Obtain an $(m-1)$-fold arithmetic progression in the starting $q$-bolck of the 
             block arithmetic progression. ›
      obtain start_m steps_m where
        step_m_pos: "i. i < m - 1  0 < steps_m i" and
        is_multi_arith_prog: "is_multi_arith_prog_on (l+1) (m - 1) 
          start_m steps_m start (start + int q - 1)" and
        g_aux: "let g = multi_arith_prog (m - 1) start_m steps_m
          in  c{0..<m - 1}  {0..l}. s<m - 1. (js. c j < l) 
          col (g c) = col (g (λi. if i  s then 0 else c i))"
        by (rule vdw_lemmaE[OF vdw_lemma_IH start_q col']) blast
        
      define g where "g = multi_arith_prog (m-1) start_m steps_m"
      have g: "col (g c) = col (g (λi. if i  s then 0 else c i))"
        if "c  {0..<(m-1)}  {0..l}" "s < m - 1" "j  s. c j < l"
        for c s using g_aux that unfolding g_def Let_def by blast

      have range_g: "g c  {start..start + int q - 1}"
        if "c  {0..<m - 1}  {0..<(l+1)}" for c
        using is_multi_arith_prog_onD[OF is_multi_arith_prog that] 
        by (auto simp: g_def)

      text ‹Obtain an $m$-fold arithmetic progression by adding the block-step.›
      define steps :: "nat  nat" where steps_def: 
        "steps = (λi.  (if i=0 then step else steps_m (i-1)))"
      define f where "f = multi_arith_prog m start_m steps"
      have f_step_g: "f c = int (c 0*step) + g (c  Suc)" for c
      proof -
        have "f c = start_m + int (i<Suc (m-1). c i * steps i)"
          using f_def unfolding multi_arith_prog_def 
          using less.prems by auto 
        also have " = start_m + int (c 0 * steps 0) + 
                       int (i<m-1. c (Suc i) * steps (Suc i))"
          using sum.lessThan_Suc_shift[where n = "m-1"] by auto
        also have " = start_m + int (c 0 * step) + 
                       int (i<m-1. c (Suc i) * steps_m i)"
          using steps_def by (auto split:if_splits)
        finally show ?thesis unfolding multi_arith_prog_def g_def 
          by simp
      qed

      text ‹ Show that this $m$-fold arithmetic progression fulfills all needed properties. ›
      have steps_gr_0: "i<m. 0 < steps i" 
        unfolding steps_def using step_m_pos prog by auto

      have is_multi_on_f: 
        "is_multi_arith_prog_on (l+1) m start_m steps a b"
      proof -
        have "a  start_m" using is_multi_arith_prog 
          unfolding is_multi_arith_prog_on_def
          using is_arith_prog_on_def prog(3) by force
        moreover {
          have "f (λ_. l) = arith_prog (g ((λ_. l)  Suc)) step l" 
            using f_step_g unfolding arith_prog_def by auto
          also have "g ((λ_. l)  Suc)  start + q" 
            using range_g[of "(λ_. l)  Suc"] by auto
          then have "arith_prog (g ((λ_. l)  Suc)) step l  
            arith_prog start step l + q"
            unfolding arith_prog_def by auto
          also have " b" using prog_in_ivl[of l]
            using is_multi_arith_prog unfolding is_multi_arith_prog_on_def
            using "1"(1) N_def by auto
          finally have "f (λ_. l)  b" by auto
         }
         ultimately show ?thesis 
           unfolding is_multi_arith_prog_on_def f_def by auto
      qed

      text ‹ Show the relational property for all $s$. ›
      have rel_prop_1: 
        "col (f c) = col (f (λi. if i  s then 0 else c i))"
        if "c  {0..<m}  {0..l}" "s<m" "js. c j < l" for c s 
      proof (cases "s = 0")
        case True
        have "c 0 < l" using that(3) True by auto
        have range_c_Suc: "c  Suc  {0..<m-1}  {0..l}" 
          using that(1) by auto
        have "f c = arith_prog (g (c  Suc)) step (c 0)" 
          using f_step_g unfolding arith_prog_def by auto
        then have "col (f c) = col (arith_prog (g (c  Suc)) step 0)"
          using one_step_more'[of "g (c  Suc)" "c 0"] c 0 < l
            range_g[of "c  Suc"] range_c_Suc 
            atLeastLessThanSuc_atLeastAtMost by auto
        also {
          have "(x<m - 1. int (c (Suc x)) * int (steps_m x)) =
                   (x=1..<m. int(c x) * int (steps x))"
            by(rule sum.reindex_bij_witness[of _ "(λx. x-1)" "Suc"]) 
              (auto simp: steps_def split:if_splits) 
          also have " = (x<m. int (if x = 0 then 0 else c x) * 
            int (steps x))" 
            by (rule sum.mono_neutral_cong_left) auto
          finally have "arith_prog (g (c  Suc)) step 0 = 
            f (λi. if i  s then 0 else c i)" 
            unfolding f_def g_def multi_arith_prog_def arith_prog_def
            using True by auto
      }
        finally show ?thesis by auto
      next 
        case False
        hence s_greater_0: "s > 0" by auto
        have range_c_Suc: "c  Suc  {0..<m-1}  {0..l}" 
          using that(1) by auto
        have "c 0 < l" using s>0 that by auto
        have g_IH:
          "col (g c') = col (g (λi. if i  s' then 0 else c' i))" 
          if "c'  {0..<m-1}  {0..l}" "s'<m-1" "js'. c' j < l" 
          for c' s' 
          using g_aux that unfolding multi_arith_prog_def g_def
          by (auto simp: Let_def)
        have g_shift_IH: "col (g (c  Suc)) = 
          col (g ((λi. if i{1..t} then 0 else c i)  Suc))" 
          if "c  {1..<m}  {0..l}" "t{1..<m}" "j{1..t}. c j < l"
          for c t
        proof -
          have "(λi. (if i  t - 1 then 0 else (c  Suc) i)) =
                (λi. (if i  {1..t} then 0 else c i))  Suc"
            using that by (auto split: if_splits simp:fun_eq_iff)
          then have right: 
            "g (λi. if i  (t-1) then 0 else (c  Suc) i) = 
             g ((λi. if i{1..t} then 0 else c i)  Suc)" by auto
          have "(c  Suc) {0..<m-1}  {0..l}" using that(1) by auto
          moreover have "t-1<m-1" using that(2) by auto
          moreover have"jt-1. (c  Suc) j < l" using that by auto
          ultimately have "col (g (c  Suc)) = 
            col (g (λi. (if i  t-1 then 0 else (c  Suc) i)))"
            using g_IH[of "(c  Suc)" "t-1"] by auto
          with right show ?thesis by auto
        qed

        have "col (f c) = col (int (c 0 * step) + g (c  Suc))" 
          using f_step_g by simp
        also have "int (c 0 * step) + g (c  Suc) = 
          arith_prog (g (c  Suc)) step (c 0)"
          by (simp add: arith_prog_def)
        also have "col  = col (arith_prog (g (c  Suc)) step 0)" 
          using one_step_more'[of "g (c  Suc)" "c 0"] c 0 < l 
            range_g[of "c  Suc"] range_c_Suc 
            atLeastLessThanSuc_atLeastAtMost by auto
        also have " = col (g (c  Suc))"
          unfolding arith_prog_def by auto
        also have " = col (g ((λi. if  i{1..s} then 0 else c i) 
          Suc))" using g_shift_IH[of "c" s] s>0 that by force
        also have " = col ((λc. int (c 0 * step) + 
          g (c  Suc))(λi. if is then 0 else c i))" 
          by (auto simp: g_def multi_arith_prog_def)
        also have " = col (f (λi. if i  s then 0 else c i))" 
          unfolding f_step_g by auto
        finally show ?thesis by simp
      qed

      show ?case
        by (rule exI[of _ start_m], rule exI[of _ steps])
           (use steps_gr_0 is_multi_on_f rel_prop_1 in 
             auto simp: f_def Let_def steps_def)
    qed
    then show ?case ..
  qed
qed




text ‹ Secondly, we show that vdw_lemma› implies the induction step of Van der Waerden's Theorem
using the pigeonhole principle. ›
lemma vdw_lemma_imp_vdw:
  assumes "vdw_lemma k k l N"
  shows   "vdw k (Suc l) N"
unfolding vdw_def proof (safe, goal_cases)
text ‹Idea: Proof uses pigeonhole principle to guarantee the existence of an arithmetic 
            progression of length $l+1$ with the same colour. ›
  case (1 a b col)
  obtain start steps where prog:
    "i. i < k  steps i > 0"
    "is_multi_arith_prog_on (l+1) k start steps a b"
    "let f = multi_arith_prog k start steps
     in  c  {0..<k}  {0..l}. s<k. ( j  s. c j < l) 
            col (f c) = col (f (λi. if i  s then 0 else c i))"
    using assms 1 
    by (elim vdw_lemmaE[where a=a and b=b and col=col and m=k 
          and k=k and l=l and n=N]) auto

  text ‹ Obtain a $k$-fold arithmetic progression $f$ of length $l$ from assumptions. ›
  define f where "f = multi_arith_prog k start steps" 
  have rel_propE: "col (f c) = col (f (λi. if i  s then 0 else c i))"
    if "c  {0..<k}  {0..l}" "s<k" " j  s. c j < l"
    for c s
    using prog(3) that unfolding f_def Let_def by auto

  text ‹There are $k+1$ values $a_r = f(0,\dots,0,l,\dots,l)$ with $0\leq r\leq k$ zeros.›
  define a_r where "a_r = (λr. f (λi. (if i<r then 0 else l)))"
  have range_col_a_r: "col (a_r x) < k" if "x < k+1" for x 
  proof -
    have "a_r x  {a..b}" unfolding a_r_def f_def 
      by (intro is_multi_arith_prog_onD[OF prog(2)]) auto
    thus ?thesis using 1 by blast
  qed
  then have "(col  a_r) ` {..<k + 1}  {..<k}" using 1(2) by auto
  then have "card ((col  a_r) ` {..<k + 1})  card {..<k}"
    by (intro card_mono) auto
  then have "¬ inj_on (col  a_r) {..<k+1}" 
    using pigeonhole[of "col  a_r" "{..<k+1}"] by auto
  text ‹Using the pigeonhole principle get $r_1$ and $r_2$ where $a_{r_1}$ and $a_{r_2}$ have the 
    same colour.›
  then obtain r1 r2 where pigeon_cols:
      "r1{..<k+1}" 
      "r2{..<k+1}" 
      "r1 < r2" 
      "(col  a_r) r1 = (col  a_r) r2"
    by (metis (mono_tags, lifting) linear linorder_inj_onI)
  text ‹ Show that the following function $h$ is an arithmetic progression which fulfills all
         properties for Van der Waerden's Theorem. ›
  define h where 
    "h = (λx. f (λi. (if i<r1 then 0 else (if i<r2 then x else l))))"
  have "h 0 = a_r r2" unfolding h_def a_r_def using r1<r2 
    by (intro arg_cong[where f = f]) auto
  moreover have "h l = a_r r1"  unfolding h_def a_r_def using r1<r2
    by (metis le_eq_less_or_eq less_le_trans)
  ultimately have "col (h 0) = col (h l)" using pigeon_cols(4) by auto
  have h_col: "col (h 0) = col (h i)" if "i{..<l+1}" for i
  proof (cases "i=l")
    case True
    then show ?thesis using col (h 0) = col (h l) by auto
  next
    case False
    then have "i<l" using that by auto
    let ?c = "(λidx. if idx < r1 then 0 else if idx < r2 then i else l)"
    have "?c{0..<k}  {0..l}" 
      using that by auto
    moreover have "(jr2-1. ?c j < l)" 
      using i<l pigeon_cols(3) by force
    ultimately have "col (f ?c) = 
      col (f (λi. if i  r2-1 then 0 else ?c i))"
      using rel_propE[of ?c "r2-1"] pigeon_cols by simp
    then show ?thesis unfolding h_def f_def 
      by (smt (verit) Nat.lessE One_nat_def add_diff_cancel_left' 
          le_less less_Suc_eq_le multi_arith_prog_mono plus_1_eq_Suc)
  qed

  define h_start where "h_start = start + l*(i{r2..<k}. steps i)"
  define h_step where "h_step = (i{r1..<r2}. steps i)"
  have h_arith_prog: "h = arith_prog h_start h_step" 
  proof -
    have "(x<k. int (if x < r1 then 0 else if x < r2 then y else l)
        * int (steps x)) =
      int l * (x = r2..<k. int (steps x)) + 
        int y * (x = r1..<r2. int (steps x))"
      for y 
    proof (cases "r2 = k")
      case True
      then have "r1<k" using pigeon_cols by auto
      with True have 
        "(x<k. int (if x < r1 then 0 else if x < r2 then y else l)
           * int (steps x)) =
         (x<k. int (if x < r1 then 0 else y) * int (steps x))"
        by (intro sum.cong) auto
      also have " = (x<r1. int (if x < r1 then 0 else y) *
          int (steps x)) + (x=r1..<k. int (if x < r1 then 0 else y)
          * int (steps x))"
        using split_sum_mid_less[of r1 k 
            "(λx. int (if x < r1 then 0 else y) * int (steps x))"] 
            r1<k by auto
      also have " = (x=r1..<k. int y * int (steps x))" by auto
      also have " = int y * (x=r1..<k. int (steps x))" 
        by (auto simp: sum_distrib_left[of "int y"])
      finally show ?thesis using True by auto
    next
      case False
      then have "r2<k" using pigeon_cols by auto
      define aux_left where "aux_left = 
        (λx. int (if x < r1 then 0 else if x < r2 then y else l)
          * int (steps x))"
      have "(x<k. aux_left x) = (x=r1..<k. aux_left x)"
        by (intro sum.mono_neutral_right) (auto simp: aux_left_def)
      also have "{r1..<k} = {r1..<r2}  {r2..<k}"
        using r1 < r2 r2 < k by auto
      also have "(x. aux_left x) = (x=r1..<r2. aux_left x) + 
        (x=r2..<k. aux_left x)"
        by (intro sum.union_disjoint) auto
      also have "(x=r1..<r2. aux_left x) =
        (x=r1..<r2. int y * int (steps x))"
        by (intro sum.cong) (auto simp: aux_left_def)
      also have "(x=r2..<k. aux_left x) = 
        (x=r2..<k. int l * int (steps x))"
        using r1 < r2 by (intro sum.cong) (auto simp: aux_left_def)
      finally show ?thesis
        by (simp add: aux_left_def sum_distrib_left)
    qed
    then show ?thesis
      unfolding arith_prog_def h_start_def h_step_def h_def f_def
        multi_arith_prog_def by (auto split:if_splits)
  qed

  define j where "j = col (h 0)"
  have case_j: "j<k" using 1 range_col_a_r col (h 0) = col (h l) 
      h l = a_r r1 j_def pigeon_cols(1) by auto
  have case_step: "h_step > 0" unfolding h_step_def
    using pigeon_cols by (intro sum_pos prog(1)) auto

  have range_h: "h i  {a..b}" if "i < l + 1" for i
    unfolding h_def f_def by (rule is_multi_arith_prog_onD[OF prog(2)])
      (use that in auto)

  have case_on: "is_arith_prog_on (l+1) h_start h_step a b"
    unfolding is_arith_prog_on_def h_arith_prog 
    using range_h[of 0] range_h[of l]
    by (auto simp: Max_ge[of "{a..b}"] Min_le[of "{a..b}"] 
        h_arith_prog arith_prog_def)

  have case_col: "h ` {..<Suc l}  col -` {j}  {a..b}" 
    using h_col range_h unfolding j_def by auto

  show ?case using case_j case_step case_on case_col 
    by (auto simp: h_arith_prog) 
qed

text ‹ Lastly, we assemble all lemmas to finally prove Van der Waerden's Theorem by induction on 
$l$. The cases $l=1$ and the induction start $l=2$ are treated separately and have been shown 
earlier.›
theorem van_der_Waerden: assumes "l>0" "k>0" shows "n. vdw k l n"
using assms proof (induction l arbitrary: k rule: less_induct)
  case (less l)
  consider  "l=1" | "l=2" | "l>2" using less.prems by linarith
  then show ?case
  proof (cases)
    assume "l=1"
    then show ?thesis using vdw_1_right by auto
  next
    assume "l=2"
    then show ?thesis using vdw_2_right by auto
  next
    assume "l > 2"
    then have "2l-1" by auto
    from less.IH[of "l-1"] l>2 
    have "k'. k'>0  n. vdw k' (l-1) n" by auto
    with vdw_imp_vdw_lemma[of "l-1" k k] l-12 k>0 
      obtain N where "vdw_lemma k k (l-1) N" by auto
    then have "vdw k l N" using vdw_lemma_imp_vdw[of k "l-1" N]
      by (simp add: less.prems(1))
    then show ?thesis by auto
  qed
qed

end