# Theory Composing

section ‹Composing functions\label{s:tm-composing}›

theory Composing
imports Elementary
begin

text ‹
For a Turing machine $M_1$ computing $f_1$ in time $T_1$ and a TM $M_2$
computing $f_2$ in time $T_2$ there is a TM $M$ computing $f_2 \circ f_1$ in
time $O(T_1(n) + \max_{m \le T_1(n)} T_2(m))$. If $T_1$ is monotone
the time bound is $O(T_1 + T_2 \circ T_1)$; if $T_1$ and $T_2$ are
polynomially bounded the running-time of $M$ is polynomially bounded, too.

The Turing machines $M_1$ and $M_2$ can have both a different alphabet and
number of tapes, so generally they cannot be composed by the $;;$ operator. To
get around this we enlarge the alphabet and prepend and append tapes, so $M$ has
as many tapes as $M_1$ and $M_2$ combined. The following function returns the
combined Turing machine $M$.

\null
›

definition "compose_machines k1 G1 M1 k2 G2 M2 ≡
enlarged G1 (append_tapes k1 (k1 + k2) M1) ;;
tm_start 1 ;;
tm_cp_until 1 k1 {□} ;;
tm_erase_cr 1 ;;
tm_start k1 ;;
prepend_tapes k1 (enlarged G2 M2) ;;
tm_cr (k1 + 1) ;;
tm_cp_until (k1 + 1) 1 {□}"

locale compose =
fixes k1 k2 G1 G2 :: nat
and M1 M2 :: machine
and T1 T2 :: "nat ⇒ nat"
and f1 f2 :: "string ⇒ string"
assumes tm_M1: "turing_machine k1 G1 M1"
and tm_M2: "turing_machine k2 G2 M2"
and computes1: "computes_in_time k1 M1 f1 T1"
and computes2: "computes_in_time k2 M2 f2 T2"
begin

definition "tm1 ≡ enlarged G1 (append_tapes k1 (k1 + k2) M1)"
definition "tm2 ≡ tm1 ;; tm_start 1"
definition "tm3 ≡ tm2 ;; tm_cp_until 1 k1 {□}"
definition "tm4 ≡ tm3 ;; tm_erase_cr 1"
definition "tm5 ≡ tm4 ;; tm_start k1"
definition "tm56 ≡ prepend_tapes k1 (enlarged G2 M2)"
definition "tm6 ≡ tm5 ;; tm56"
definition "tm7 ≡ tm6 ;; tm_cr (k1 + 1)"
definition "tm8 ≡ tm7 ;; tm_cp_until (k1 + 1) 1 {□}"

definition G :: nat where
"G ≡ max G1 G2"

lemma G1: "G1 ≤ G" and G2: "G2 ≤ G"
using G_def by simp_all

lemma k_ge: "k1 ≥ 2" "k2 ≥ 2"
using tm_M1 tm_M2 turing_machine_def by simp_all

lemma tm1_tm: "turing_machine (k1 + k2) G tm1"
unfolding tm1_def using turing_machine_enlarged append_tapes_tm tm_M1 G1 by simp

lemma tm2_tm: "turing_machine (k1 + k2) G tm2"
unfolding tm2_def using tm1_tm tm_start_tm turing_machine_def by blast

lemma tm3_tm: "turing_machine (k1 + k2) G tm3"
unfolding tm3_def
using tm2_tm tm_cp_until_tm turing_machine_def k_ge turing_machine_sequential_turing_machine

lemma tm4_tm: "turing_machine (k1 + k2) G tm4"
unfolding tm4_def
using tm3_tm tm_erase_cr_tm turing_machine_def turing_machine_sequential_turing_machine
by (metis Suc_1 Suc_le_lessD tm_erase_cr_tm zero_less_one)

lemma tm5_tm: "turing_machine (k1 + k2) G tm5"
unfolding tm5_def
using tm4_tm tm_start_tm turing_machine_def turing_machine_sequential_turing_machine
by auto

lemma tm6_tm: "turing_machine (k1 + k2) G tm6"
unfolding tm6_def
using tm5_tm tm56_def turing_machine_enlarged prepend_tapes_tm tm_M2 G2
by simp

lemma tm7_tm: "turing_machine (k1 + k2) G tm7"
unfolding tm7_def using tm6_tm tm_cr_tm turing_machine_def by blast

lemma tm8_tm: "turing_machine (k1 + k2) G tm8"
unfolding tm8_def
using tm7_tm tm_cp_until_tm turing_machine_def turing_machine_sequential_turing_machine k_ge(2)
verit_comp_simplify1(3) zero_less_one)

context
fixes x :: string
begin

definition "zs ≡ string_to_symbols x"

lemma bit_symbols_zs: "bit_symbols zs"
using zs_def by simp

abbreviation "n ≡ length x"

lemma length_zs [simp]: "length zs = n"
using zs_def by simp

definition "tps0 ≡ snd (start_config (k1 + k2) zs)"

definition tps1a :: "tape list" where
"tps1a ≡ SOME tps. tps ::: 1 = string_to_contents (f1 x) ∧
transforms M1 (snd (start_config k1 (string_to_symbols x))) (T1 n) tps"

lemma tps1a_aux:
"tps1a ::: 1 = string_to_contents (f1 x)"
"transforms M1 (snd (start_config k1 (string_to_symbols x))) (T1 n) tps1a"
using tps1a_def someI_ex[OF computes_in_timeD[OF computes1, of x]]
by simp_all

lemma tps1a:
"tps1a ::: 1 = string_to_contents (f1 x)"
"transforms M1 (snd (start_config k1 zs)) (T1 n) tps1a"
using tps1a_aux zs_def by simp_all

lemma length_tps1a [simp]: "length tps1a = k1"
using tps1a(2) tm_M1 start_config_length execute_num_tapes transforms_def transits_def turing_machine_def
by (smt (verit, del_insts) Suc_1 add_pos_pos less_le_trans less_numeral_extra(1) plus_1_eq_Suc snd_conv)

definition tps1b :: "tape list" where
"tps1b ≡ replicate k2 (⌊[]⌋, 0)"

definition tps1 :: "tape list" where
"tps1 ≡ tps1a @ tps1b"

lemma tps1_at_1: "tps1 ! 1 = tps1a ! 1"
using tps1_def length_tps1a k_ge by (metis Suc_1 Suc_le_lessD nth_append)

lemma tps1_at_1': "tps1 ::: 1 = string_to_contents (f1 x)"
using tps1_at_1 tps1a by simp

lemma tps1_pos_le: "tps1 :#: 1 ≤ T1 n"
proof -
have "execute M1 (start_config k1 zs) (T1 n) = (length M1, tps1a)"
using transforms_def transits_def tps1a(2)
by (metis (no_types, lifting) execute_after_halting_ge fst_conv start_config_def snd_conv)
moreover have "execute M1 (start_config k1 zs) (T1 n) <#> 1 ≤ T1 n"
using head_pos_le_time[OF tm_M1, of 1] k_ge by fastforce
ultimately show ?thesis
using tps1_at_1 by simp
qed

lemma length_f1_x: "length (f1 x) ≤ T1 n"
proof -
have "execute M1 (start_config k1 zs) (T1 n) = (length M1, tps1a)"
using transforms_def transits_def tps1a(2)
by (metis (no_types, lifting) execute_after_halting_ge fst_conv start_config_def snd_conv)
moreover have "(execute M1 (start_config k1 zs) (T1 n) <:> 1) i = □" if "i > T1 n" for i
using blank_after_time[OF that _ _ tm_M1] k_ge(1) by simp
ultimately have "(tps1a ::: 1) i = □" if "i > T1 n" for i
using that by simp
then have "(string_to_contents (f1 x)) i = □" if "i > T1 n" for i
using that tps1a(1) by simp
then have "length (string_to_symbols (f1 x)) ≤ T1 n"
by (metis length_map order_refl verit_comp_simplify1(3) zero_neq_numeral zero_neq_one)
then show ?thesis
by simp
qed

lemma start_config_append:
"start_config (k1 + k2) zs = (0, snd (start_config k1 zs) @ tps1b)"
proof
have "k1 > 0"
using tm_M1 turing_machine_def by simp
show "fst (start_config (k1 + k2) zs) = fst (0, snd (start_config k1 zs) @ tps1b)"
using start_config_def by simp
show "snd (start_config (k1 + k2) zs) = snd (0, snd (start_config k1 zs) @ tps1b)"
(is "?l = ?r")
proof (rule nth_equalityI)
have len: "||start_config k1 zs|| = k1"
using start_config_length by (simp add: ‹0 < k1›)
show "length ?l = length ?r"
using start_config_length tps1b_def tm_M1 turing_machine_def by simp
show "?l ! j = ?r ! j" if "j < length ?l" for j
proof (cases "j < k1")
case True
show ?thesis
proof (cases "j = 0")
case True
then show ?thesis
using start_config_def k1 > 0 by simp
next
case False
then have 1: "?l ! j = (λi. if i = 0 then ▹ else □, 0)"
using start_config_def k1 > 0 True by auto
have "?r ! j = snd (start_config k1 zs) ! j"
using True len by (simp add: nth_append)
then have "?r ! j = (λi. if i = 0 then ▹ else □, 0)"
using start_config4 k1 > 0 False True by simp
then show ?thesis
using 1 by simp
qed
next
case False
then have j: "j < k1 + k2" "k1 ≤ j"
using that ‹0 < k1› add_gr_0 start_config_length by simp_all
then have "?r ! j = (⌊[]⌋, 0)"
using tps1b_def by (simp add: False len nth_append)
moreover have "?l ! j = (λi. if i = 0 then ▹ else □, 0)"
using start_config4 k1 > 0 j by simp
ultimately show ?thesis
by auto
qed
qed
qed

lemma tm1 [transforms_intros]: "transforms tm1 tps0 (T1 n) tps1"
proof -
let ?M = "append_tapes k1 (k1 + length tps1b) M1"
have len: "||start_config k1 zs|| = k1"
using start_config_length[of k1 zs] tm_M1 turing_machine_def by simp
have "transforms ?M (snd (start_config k1 zs) @ tps1b) (T1 n) (tps1a @ tps1b)"
using transforms_append_tapes[OF tm_M1 len tps1a(2), of tps1b] .
moreover have "tps0 = snd (start_config k1 zs) @ tps1b"
unfolding tps0_def using start_config_append by simp
ultimately have *: "transforms ?M tps0 (T1 n) tps1"
using tps1_def by simp

have "symbols_lt G1 zs"
using bit_symbols_zs tm_M1 turing_machine_def by auto
moreover have "turing_machine (k1 + k2) G1 ?M"
using append_tapes_tm[OF tm_M1, of "k1 + k2"] by (simp add: tps1b_def)
ultimately have "transforms (enlarged G1 ?M) tps0 (T1 n) tps1"
using transforms_enlarged * tps0_def by simp
then show ?thesis
using tm1_def tps1b_def by simp
qed

lemma clean_string_to_contents: "clean_tape (string_to_contents xs, i)"
using clean_tape_def by simp

definition tps2 :: "tape list" where
"tps2 ≡ tps1 [1 := tps1 ! 1 |#=| 0]"

lemma length_tps2 [simp]: "length tps2 = k1 + k2"
using tps2_def tps1_def by (simp add: tps1b_def)

lemma tm2:
assumes "t = Suc (T1 n + tps1 :#: Suc 0)"
shows "transforms tm2 tps0 t tps2"
unfolding tm2_def
proof (tform tps: assms tps2_def)
show "1 < length tps1"
using tm_M1 turing_machine_def tps1_def by simp
show "clean_tape (tps1 ! 1)"
using tps1a(1) tps1_at_1 clean_tape_def by simp
qed

corollary tm2' [transforms_intros]:
assumes "t = Suc (2 * T1 n)"
shows "transforms tm2 tps0 t tps2"
using assms tm2 tps1_pos_le transforms_monotone by simp

definition tps3 :: "tape list" where
"tps3 ≡ tps2 [1 := tps2 ! 1 |#=| (Suc (length (f1 x))), k1 := tps2 ! 1 |#=| (Suc (length (f1 x)))]"

lemma tm3:
assumes "t = Suc (Suc (2 * T1 n + Suc (length (f1 x))))"
shows "transforms tm3 tps0 t tps3"
unfolding tm3_def
proof (tform tps: k_ge)
have "Suc 0 < k1 + k2" "0 < k2"
using k_ge by simp_all
then have *: "tps2 ! 1 = tps1 ! 1 |#=| 0"
using tps2_def by (simp add: tps1_def tps1b_def)
let ?i = "Suc (length (f1 x))"
show "rneigh (tps2 ! 1) {0} ?i"
using * tps1_at_1 tps1a by (intro rneighI) auto
show "tps3 = tps2
[1 := tps2 ! 1 |+| Suc (length (f1 x)),
k1 := implant (tps2 ! 1) (tps2 ! k1) (Suc (length (f1 x)))]"
proof -
have "tps2 ! 1 |#=| (Suc (length (f1 x))) = tps2 ! 1 |#=| Suc (tps2 :#: 1 + length (f1 x))"
by (metis "*" One_nat_def add_Suc plus_1_eq_Suc snd_conv)
moreover have "tps2 ! 1 |#=| ?i = implant (tps2 ! 1) (tps2 ! k1) ?i"
proof
have 1: "tps2 ! 1 = (string_to_contents (f1 x), 0)"
using tps1_at_1' * by simp
have "tps1 ! k1 = (⌊[]⌋, 0)"
using tps1_def tps1b_def by (simp add: ‹0 < k2› nth_append)
then have 2: "tps2 ! k1 = (⌊[]⌋, 0)"
using tps2_def k_ge by simp
then show "snd (tps2 ! 1 |#=| ?i) = snd (implant (tps2 ! 1) (tps2 ! k1) ?i)"
using implant by simp
have "fst (implant (tps2 ! 1) (tps2 ! k1) ?i) i = fst (tps2 ! 1 |#=| ?i) i" for i
using 1 2 implant by simp
then show "fst (tps2 ! 1 |#=| ?i) = fst (implant (tps2 ! 1) (tps2 ! k1) ?i)"
by auto
qed
ultimately show ?thesis
using tps3_def by simp
qed
show "t = Suc (2 * T1 n) + Suc (Suc (length (f1 x)))"
using assms by simp
qed

definition "tps3' ≡ tps1a
[1 := (string_to_contents (f1 x), Suc (length (f1 x)))] @
((string_to_contents (f1 x), Suc (length (f1 x))) #
replicate (k2 - 1) (⌊[]⌋, 0))"

lemma tps3': "tps3 = tps3'"
proof (rule nth_equalityI)
have "length tps3 = k1 + k2"
using tps3_def by simp
moreover have "length tps3' = k1 + k2"
using k_ge(2) tps3'_def by simp
ultimately show "length tps3 = length tps3'"
by simp
show "tps3 ! j = tps3' ! j" if "j < length tps3" for j
proof (cases "j < k1")
case True
then have rhs: "tps3' ! j = (tps1a [1 := (string_to_contents (f1 x), Suc (length (f1 x)))]) ! j"
show ?thesis
proof (cases "j = 1")
case True
then have "tps3 ! j = tps2 ! 1 |#=| (Suc (length (f1 x)))"
using tps3_def Suc_1 Suc_n_not_le_n ‹length tps3 = k1 + k2› k_ge(1)
length_tps2 nth_list_update_eq nth_list_update_neq that
by auto
then have "tps3 ! j = tps1 ! 1 |#=| (Suc (length (f1 x)))"
using tps2_def True ‹length tps3 = k1 + k2› length_tps2 that by auto
then have "tps3 ! j = (string_to_contents (f1 x), Suc (length (f1 x)))"
using tps1_at_1 tps1a(1) by simp
then show ?thesis
using rhs True k_ge(1) by auto
next
case False
then have "tps3 ! j = tps2 ! j"
using tps3_def True by simp
then have "tps3 ! j = tps1 ! j"
using tps2_def False by simp
then have "tps3 ! j = tps1a ! j"
using length_tps1a tps1_def False True by (simp add: nth_append)
moreover have "tps3' ! j = tps1a ! j"
using False rhs by simp
ultimately show ?thesis
by simp
qed
next
case j_ge: False
show ?thesis
proof (cases "j = k1")
case True
then have "tps3 ! j = tps2 ! 1 |#=| (Suc (length (f1 x)))"
using tps3_def that by simp
then have "tps3 ! j = tps1 ! 1 |#=| (Suc (length (f1 x)))"
using tps2_def ‹length tps3 = k1 + k2› length_tps2 Suc_1 Suc_le_lessD tm1_tm turing_machine_def
by simp
then have "tps3 ! j = (string_to_contents (f1 x), Suc (length (f1 x)))"
using tps1_at_1 tps1a(1) by simp
moreover have "tps3' ! j = (string_to_contents (f1 x), Suc (length (f1 x)))"
using True tps3'_def
by (metis (no_types, lifting) length_list_update length_tps1a nth_append_length)
ultimately show ?thesis
by simp
next
case False
then have "j > k1"
using j_ge by simp
then have "tps3' ! j = ((string_to_contents (f1 x), Suc (length (f1 x))) #
replicate (k2 - 1) (⌊[]⌋, 0)) ! (j - k1)"
moreover have "j - k1 < k2"
by (metis ‹k1 < j› ‹length tps3 = k1 + k2› add.commute less_diff_conv2 less_imp_le that)
ultimately have *: "tps3' ! j = (⌊[]⌋, 0)"
nth_Cons_pos nth_replicate plus_1_eq_Suc zero_less_diff)
have "tps3 ! j = tps2 ! j"
using tps3_def ‹k1 < j› k_ge(1) by simp
then have "tps3 ! j = tps1 ! j"
using tps2_def ‹k1 < j› k_ge(1) by simp
then have "tps3 ! j = tps1b ! (j - k1)"
using tps1_def by (simp add: j_ge nth_append)
then have "tps3 ! j = (⌊[]⌋, 0)"
using tps1b_def by (simp add: ‹j - k1 < k2›)
then show ?thesis
using * by simp
qed
qed
qed

lemma tm3' [transforms_intros]:
assumes "t = Suc (Suc (Suc (3 * T1 n)))"
shows "transforms tm3 tps0 t tps3'"
proof -
have "transforms tm3 tps0 (Suc (Suc (2 * T1 n + Suc (length (f1 x))))) tps3"
using tm3 by simp
moreover have "t ≥ Suc (Suc (2 * T1 n + Suc (length (f1 x))))"
using assms length_f1_x by simp
ultimately show ?thesis
using tps3' transforms_monotone by auto
qed

definition "tps4 ≡
tps1a [1 := (⌊[]⌋, 1)] @
((string_to_contents (f1 x), Suc (length (f1 x))) #
replicate (k2 - 1) (⌊[]⌋, 0))"

lemma tm4:
assumes "t = 9 + (3 * T1 n + (Suc (3 * length (string_to_symbols (f1 x)))))"
shows "transforms tm4 tps0 t tps4"
unfolding tm4_def
proof (tform)
show "1 < length tps3'"
using tps3'_def using tm1_tm turing_machine_def by auto
let ?zs = "string_to_symbols (f1 x)"
show "proper_symbols ?zs"
by simp
show "tps4 = tps3'[1 := (⌊[]⌋, 1)]"
using tps4_def tps3'_def k_ge(1) length_tps1a by (simp add: list_update_append1)
show "tps3' ::: 1 = ⌊string_to_symbols (f1 x)⌋"
proof -
have "tps3' ! 1 = (string_to_contents (f1 x), Suc (length (f1 x)))"
using tps3'_def k_ge(1) length_tps1a by (simp add: nth_append)
then show ?thesis
by auto
qed
have "tps3' :#: 1 = Suc (length (f1 x))"
using tps3'_def k_ge(1) length_tps1a by (simp add: nth_append)
then show "t = Suc (Suc (Suc (3 * T1 n))) +
(tps3' :#: 1 + 2 * length (string_to_symbols (f1 x)) + 6)"
using assms by simp
qed

lemma tm4' [transforms_intros]:
assumes "t = 10 + (6 * T1 n)"
shows "transforms tm4 tps0 t tps4"
proof (rule transforms_monotone[OF tm4], simp)
show "10 + (3 * T1 n + 3 * length (f1 x)) ≤ t"
using length_f1_x assms by simp
qed

definition "tps5 ≡
tps1a [1 := (⌊[]⌋, 1)] @
((string_to_contents (f1 x), 0) #
replicate (k2 - 1) (⌊[]⌋, 0))"

lemma tm5:
assumes "t = 11 + (6 * T1 n + tps4 :#: k1)"
shows "transforms tm5 tps0 t tps5"
unfolding tm5_def
proof (tform time: assms)
show "k1 < length tps4"
using tps4_def length_tps1a by simp
show "tps5 = tps4[k1 := tps4 ! k1 |#=| 0]"
using tps4_def tps5_def length_tps1a
by (metis (no_types, lifting) fst_conv length_list_update list_update_length nth_append_length)
show "clean_tape (tps4 ! k1)"
using tps4_def length_tps1a clean_tape_def
fst_conv length_list_update nat.distinct(1) nat_1_add_1 nth_append_length numeral_3_eq_3)
qed

abbreviation "ys ≡ string_to_symbols (f1 x)"

abbreviation "m ≡ length (f1 x)"

definition "tps5' ≡
tps1a [1 := (⌊[]⌋, 1)] @
snd (start_config k2 ys)"

lemma tps5': "tps5 = tps5'"
using tps5_def tps5'_def start_config_def by auto

lemma tm5' [transforms_intros]:
assumes "t = 12 + 7 * T1 n"
shows "transforms tm5 tps0 t tps5'"
proof -
have "tps4 :#: k1 = Suc (length (f1 x))"
using tps4_def
by (metis (no_types, lifting) length_list_update length_tps1a nth_append_length snd_conv)
then have "tps4 :#: k1 ≤ Suc (T1 n)"
using length_f1_x by simp
then have "t ≥ 11 + (6 * T1 n + tps4 :#: k1)"
using assms by simp
then show ?thesis
using tm5 transforms_monotone tps5' by simp
qed

definition tps6b :: "tape list" where
"tps6b ≡ SOME tps. tps ::: 1 = string_to_contents (f2 (f1 x)) ∧
transforms M2 (snd (start_config k2 ys)) (T2 m) tps"

lemma tps6b:
"tps6b ::: 1 = string_to_contents (f2 (f1 x))"
"transforms M2 (snd (start_config k2 ys)) (T2 m) tps6b"
using tps6b_def someI_ex[OF computes_in_timeD[OF computes2, of "f1 x"]]
by simp_all

lemma tps6b_pos_le: "tps6b :#: 1 ≤ T2 m"
proof -
have "execute M2 (start_config k2 ys) (T2 m) = (length M2, tps6b)"
using transforms_def transits_def tps6b(2)
by (metis (no_types, lifting) execute_after_halting_ge fst_conv start_config_def snd_conv)
moreover have "execute M2 (start_config k2 ys) (T2 m) <#> 1 ≤ T2 m"
using head_pos_le_time[OF tm_M2, of 1] k_ge by simp
ultimately show ?thesis
by simp
qed

lemma length_tps6b: "length tps6b = k2"
using tm_M2 execute_num_tapes k_ge(2) tps5' tps5'_def tps5_def tps6b(2) transforms_def transits_def
by (smt (verit, ccfv_threshold) One_nat_def Suc_diff_Suc length_Cons length_replicate less_le_trans
minus_nat.diff_0 numeral_2_eq_2 prod.sel(2) same_append_eq zero_less_Suc)

lemma length_f2_f1_x: "length (f2 (f1 x)) ≤ T2 m"
proof -
have "execute M2 (start_config k2 ys) (T2 m) = (length M2, tps6b)"
using transforms_def transits_def tps6b(2)
by (metis (no_types, lifting) execute_after_halting_ge fst_conv start_config_def snd_conv)
moreover have "(execute M2 (start_config k2 ys) (T2 m) <:> 1) i = 0" if "i > T2 m" for i
using blank_after_time[OF that _ _ tm_M2] k_ge(2) by simp
ultimately have "(tps6b ::: 1) i = □" if "i > T2 m" for i
using that by simp
then have "(string_to_contents (f2 (f1 x))) i = □" if "i > T2 m" for i
using that tps6b(1) by simp
then have "length (string_to_symbols (f2 (f1 x))) ≤ T2 m"
by (metis length_map order_refl verit_comp_simplify1(3) zero_neq_numeral zero_neq_one)
then show ?thesis
by simp
qed

lemma enlarged_M2: "transforms (enlarged G2 M2) (snd (start_config k2 ys)) (T2 m) tps6b"
proof -
have "symbols_lt G2 (string_to_symbols (f1 x))"
using tm_M2 turing_machine_def by simp
then show ?thesis
using transforms_enlarged[OF tm_M2 _ tps6b(2)] by simp
qed

lemma enlarged_M2_tm: "turing_machine k2 G (enlarged G2 M2)"
using turing_machine_enlarged tm_M2 G2 by simp

definition "tps6 ≡ tps1a[1 := (⌊[]⌋, 1)] @ tps6b"

lemma tm56 [transforms_intros]: "transforms tm56 tps5' (T2 m) tps6"
using transforms_prepend_tapes[OF enlarged_M2_tm _ _ enlarged_M2, of "tps1a [1 := (⌊[]⌋, 1)]" k1]
tps5'_def tps6_def tm56_def start_config_length k_ge(2)
by auto

lemma tps6_at_Suc_k1: "tps6 ::: (k1 + 1) = string_to_contents (f2 (f1 x))"
and tps6_pos_le: "tps6 :#: (k1 + 1) ≤ T2 m"
proof -
have "tps6 ! (k1 + 1) = tps6b ! 1"
using tps6_def length_tps1a length_tps6b by (simp add: nth_append)
then show
"tps6 ::: (k1 + 1) = string_to_contents (f2 (f1 x))"
"tps6 :#: (k1 + 1) ≤ T2 m"
using tps6b(1) tps6b_pos_le by simp_all
qed

lemma tm6 [transforms_intros]:
assumes "t = 12 + 7 * T1 n + T2 m"
shows "transforms tm6 tps0 t tps6"
unfolding tm6_def by (tform tps: assms)

definition "tps7 ≡
tps1a[1 := (⌊[]⌋, 1)] @
tps6b[1 := (string_to_contents (f2 (f1 x)), 1)]"

lemma tps7_at_Suc_k1: "tps7 ! (k1 + 1) = (string_to_contents (f2 (f1 x)), 1)"
using tps7_def k_ge(2) length_tps1a length_tps6b

lemma tm7:
assumes "t = 14 + (7 * T1 n + (T2 m + tps6 :#: Suc k1))"
shows "transforms tm7 tps0 t tps7"
unfolding tm7_def
proof (tform time: assms)
show "k1 + 1 < length tps6"
using tps6_def k_ge(2) length_tps1a length_tps6b by simp
show "clean_tape (tps6 ! (k1 + 1))"
using tps6_at_Suc_k1 clean_tape_def by simp
show "tps7 = tps6[k1 + 1 := tps6 ! (k1 + 1) |#=| 1]"
proof -
have "tps6 ! (k1 + 1) |#=| 1 = (string_to_contents (f2 (f1 x)), 1)"
using tps6_at_Suc_k1 by simp
then show ?thesis
using tps6_def tps7_def length_tps1a length_tps6b k_ge tps7_at_Suc_k1
qed
qed

corollary tm7' [transforms_intros]:
assumes "t = 14 + 7 * T1 n + 2 * T2 m"
shows "transforms tm7 tps0 t tps7"
proof (rule transforms_monotone[OF tm7], simp)
show "14 + (7 * T1 n + (T2 (length (f1 x)) + tps6 :#: Suc k1)) ≤ t"
using assms tps6_pos_le by simp
qed

definition "tps8 ≡
tps1a[1 := (string_to_contents (f2 (f1 x)), Suc (length (f2 (f1 x))))] @
tps6b[1 := (string_to_contents (f2 (f1 x)), Suc (length (f2 (f1 x))))]"

lemma tps8_at_1: "tps8 ::: 1 = string_to_contents (f2 (f1 x))"
using tps8_def length_tps1a k_ge(1)
by (metis (no_types, lifting) One_nat_def Suc_le_lessD length_list_update nth_append
nth_list_update_eq numeral_2_eq_2 prod.sel(1))

lemma tm8:
assumes "t = 15 + 7 * T1 n + 2 * T2 m + length (f2 (f1 x))"
shows "transforms tm8 tps0 t tps8"
unfolding tm8_def
proof (tform tps: assms)
show "k1 + 1 < length tps7"
using tps7_def length_tps1a length_tps6b k_ge(2) by simp
show "1 < length tps7"
using tps7_def length_tps6b k_ge(2) by simp
let ?n = "length (f2 (f1 x))"
show "rneigh (tps7 ! (k1 + 1)) {□} ?n"
proof (rule rneighI)
show "(tps7 ::: (k1 + 1)) (tps7 :#: (k1 + 1) + ?n) ∈ {□}"
using tps7_at_Suc_k1 by simp
show "⋀n'. n' < ?n ⟹ (tps7 ::: (k1 + 1)) (tps7 :#: (k1 + 1) + n') ∉ {□}"
using tps7_at_Suc_k1 by simp
qed
show "tps8 = tps7
[k1 + 1 := tps7 ! (k1 + 1) |+| ?n,
1 := implant (tps7 ! (k1 + 1)) (tps7 ! 1) ?n]"
(is "tps8 = ?tps")
proof (rule nth_equalityI)
show "length tps8 = length ?tps"
using tps8_def tps7_def by simp
have len: "length tps8 = k1 + k2"
using tps8_def length_tps6b by simp
show "tps8 ! j = ?tps ! j" if "j < length tps8" for j
proof (cases "j < k1")
case j_less: True
then have lhs: "tps8 ! j = tps1a[1 := (string_to_contents (f2 (f1 x)), Suc (length (f2 (f1 x))))] ! j"
using tps8_def length_tps1a length_tps6b k_ge by (simp add: nth_append)
show ?thesis
proof (cases "j = 1")
case True
then have 1: "?tps ! j = implant (tps7 ! (k1 + 1)) (tps7 ! 1) ?n"
using ‹1 < length tps7› by simp
have 2: "tps8 ! j = (string_to_contents (f2 (f1 x)), Suc (length (f2 (f1 x))))"
using lhs True j_less by simp
have 3: "tps7 ! 1 = (⌊[]⌋, 1)"
using tps7_def length_tps1a
by (metis (no_types, lifting) True j_less length_list_update nth_append nth_list_update_eq)
have "implant (string_to_contents (f2 (f1 x)), 1) (⌊[]⌋, 1) ?n =
(string_to_contents (f2 (f1 x)), Suc ?n)"
using implant contents_def by auto
then show ?thesis
using 1 2 3 tps7_at_Suc_k1 by simp
next
case False
then have "?tps ! j = tps7 ! j"
then have "?tps ! j = tps1a ! j"
using False j_less tps7_def length_tps1a
by (metis (no_types, lifting) length_list_update nth_append nth_list_update_neq)
moreover have "tps8 ! j = tps1a ! j"
using False j_less tps8_def lhs by simp
ultimately show ?thesis
by simp
qed
next
case j_ge: False
then have lhs: "tps8 ! j = tps6b[1 := (string_to_contents (f2 (f1 x)), Suc (length (f2 (f1 x))))] ! (j - k1)"
using tps8_def length_tps1a length_tps6b k_ge by (simp add: nth_append)
show ?thesis
proof (cases "j = Suc k1")
case True
then have "tps8 ! j = (string_to_contents (f2 (f1 x)), Suc (length (f2 (f1 x))))"
using lhs len that length_tps6b by simp
moreover have "?tps ! j = tps7 ! Suc k1 |+| ?n"
using True ‹k1 + 1 < length tps7› k_ge(1) by simp
ultimately show ?thesis
using tps7_at_Suc_k1 True by simp
next
case False
then have "tps8 ! j = tps6b ! (j - k1)"
using lhs by simp
moreover have "?tps ! j = tps7 ! j"
using False j_ge that k_ge(1) by simp
ultimately show ?thesis
using tps7_def j_ge False length_tps1a
nth_append nth_list_update_neq plus_1_eq_Suc)
qed
qed
qed
qed

lemma tm8':
assumes "t = 15 + 7 * T1 n + 3 * T2 m"
shows "transforms tm8 tps0 t tps8"
proof (rule transforms_monotone[OF tm8], simp)
show "15 + 7 * T1 n + 2 * T2 m + length (f2 (f1 x)) ≤ t"
using length_f2_f1_x assms by simp
qed

lemma tm8'_mono:
assumes "mono T2"
and "t = 15 + 7 * T1 n + 3 * T2 (T1 n)"
shows "transforms tm8 tps0 t tps8"
proof (rule transforms_monotone[OF tm8'], simp)
have "T2 (T1 n) ≥ T2 m"
using assms(1) length_f1_x monoE by auto
then show "15 + 7 * T1 n + 3 * T2 m ≤ t"
using assms(2) by simp
qed

end  (* context x *)

lemma computes_composed_mono:
assumes "mono T2" and "T = (λn. 15 + 7 * T1 n + 3 * T2 (T1 n))"
shows "computes_in_time (k1 + k2) tm8 (f2 ∘ f1) T"
proof
fix x
have "tps8 x ::: 1 = string_to_contents (f2 (f1 x))"
using tps8_at_1 by simp
moreover have "transforms tm8 (snd (start_config (k1 + k2) (string_to_symbols x))) (T (length x)) (tps8 x)"
using tm8'_mono assms tps0_def zs_def by simp
ultimately show "∃tps.
tps ::: 1 = string_to_contents ((f2 ∘ f1) x) ∧
transforms tm8 (snd (start_config (k1 + k2) (string_to_symbols x))) (T (length x)) tps"
by force
qed

end  (* locale compose *)

lemma computes_composed_poly:
assumes tm_M1: "turing_machine k1 G1 M1"
and tm_M2: "turing_machine k2 G2 M2"
and computes1: "computes_in_time k1 M1 f1 T1"
and computes2: "computes_in_time k2 M2 f2 T2"
assumes "big_oh_poly T1" and "big_oh_poly T2"
shows "∃T k G M. big_oh_poly T ∧ turing_machine k G M ∧ computes_in_time k M (f2 ∘ f1) T"
proof -
obtain d1 :: nat where "big_oh T1 (λn. n ^ d1)"
using assms(5) big_oh_poly_def by auto
obtain b c d2 :: nat where cm: "d2 > 0" "∀n. T2 n ≤ b + c * n ^ d2"
using big_oh_poly_offset[OF assms(6)] by auto
let ?U = "λn. b + c * n ^ d2"
have U: "T2 n ≤ ?U n" for n
using cm by simp
have "mono ?U"
have computesU: "computes_in_time k2 M2 f2 ?U"
using computes_in_time_mono[OF computes2 U] .
interpret compo: compose k1 k2 G1 G2 M1 M2 T1 ?U f1 f2
using assms computesU compose.intro by simp
let ?M = "compo.tm8"
let ?T = "(λn. 15 + 7 * T1 n + 3 * (b + c * T1 n ^ d2))"
have "computes_in_time (k1 + k2) ?M (f2 ∘ f1) ?T"
using compo.computes_composed_mono[OF mono ?U, of ?T] by simp
moreover have "big_oh_poly ?T"
proof -
have "big_oh_poly (λn. n ^ d2)"
using big_oh_poly_poly by simp
moreover have "(λn. T1 n ^ d2) = (λn. n ^ d2) ∘ T1"
by auto
ultimately have "big_oh_poly (λn. T1 n ^ d2)"
using big_oh_poly_composition[OF assms(5)] by auto
then have "big_oh_poly (λn. 3 * (b + c * T1 n ^ d2))"
using big_oh_poly_const big_oh_poly_prod big_oh_poly_sum by simp
then show ?thesis
using assms(5) big_oh_poly_const big_oh_poly_prod big_oh_poly_sum by simp
qed
moreover have "turing_machine (k1 + k2) compo.G ?M"
using compo.tm8_tm .
ultimately show ?thesis
by auto
qed

end