Theory ReadShow

section‹Converting Types to Strings and Back Again›
theory ReadShow
  imports 
    Solidity_Symbex 
begin 

text‹
  In the following, we formalize a family of projection (and injection) functions for injecting 
  (projecting) basic types (i.e., @{type ‹nat›}, @{type ‹int›}, and @{type ‹bool›} in (out) of the 
  domains of strings. We provide variants for the two string representations of Isabelle/HOL, namely
  @{type ‹string›} and @{type ‹String.literal›}. 
›


subsubsection‹Bool›
definition 
 ‹Readbool s = (if s = ''True'' then True else False)›
definition
 ‹Showbool b = (if b then ''True'' else ''False'')›
definition 
 ‹STR_is_bool s = (Showbool (Readbool s) = s)›

declare Readbool_def [solidity_symbex]
        Showbool_def [solidity_symbex]

lemma Show_Read_bool_id: ‹STR_is_bool s ⟹ (Showbool (Readbool s) = s)›
  using STR_is_bool_def by fastforce

lemma STR_is_bool_split: ‹STR_is_bool s ⟹ s = ''False'' ∨ s = ''True''›
  by(auto simp: STR_is_bool_def Readbool_def Showbool_def)

lemma Read_Show_bool_id: ‹Readbool (Showbool b) = b›
  by(auto simp: Readbool_def Showbool_def)

definition ReadLbool::‹String.literal ⇒ bool› (‹⌊_⌋›) where 
 ‹ReadLbool s = (if s = STR ''True'' then True else False)›
definition ShowLbool:: ‹bool ⇒ String.literal› (‹⌈_⌉›) where
 ‹ShowLbool b = (if b then STR ''True'' else STR ''False'')›
definition 
 ‹strL_is_bool' s = (ShowLbool (ReadLbool s) = s)›

declare ReadLbool_def [solidity_symbex]
        ShowLbool_def [solidity_symbex]


lemma Show_Read_bool'_id: ‹strL_is_bool' s ⟹ (ShowLbool (ReadLbool s) = s)›
  using strL_is_bool'_def by fastforce

lemma strL_is_bool'_split:  ‹strL_is_bool' s ⟹ s = STR ''False'' ∨ s = STR ''True''›
  by(auto simp: strL_is_bool'_def ReadLbool_def ShowLbool_def)

lemma Read_Show_bool'_id[simp]: ‹ReadLbool (ShowLbool b) = b›
  by(auto simp: ReadLbool_def ShowLbool_def)

lemma true_neq_false[simp]: "ShowLbool True ≠ ShowLbool False"
  by (metis Read_Show_bool'_id)

subsubsection‹Natural Numbers›

definition  nat_of_digit ::  ‹char ⇒ nat› where
  ‹nat_of_digit c = 
    (if c = CHR ''0'' then 0
    else if c = CHR ''1'' then 1
    else if c = CHR ''2'' then 2
    else if c = CHR ''3'' then 3
    else if c = CHR ''4'' then 4
    else if c = CHR ''5'' then 5
    else if c = CHR ''6'' then 6
    else if c = CHR ''7'' then 7
    else if c = CHR ''8'' then 8
    else if c = CHR ''9'' then 9
    else undefined)›

declare nat_of_digit_def [solidity_symbex]

definition  is_digit ::  ‹char ⇒ bool› where
 ‹is_digit c = 
    (if c = CHR ''0'' then True
    else if c = CHR ''1'' then True
    else if c = CHR ''2'' then True
    else if c = CHR ''3'' then True
    else if c = CHR ''4'' then True
    else if c = CHR ''5'' then True
    else if c = CHR ''6'' then True
    else if c = CHR ''7'' then True
    else if c = CHR ''8'' then True
    else if c = CHR ''9'' then True
    else if c = CHR ''-'' then True
    else False)›



definition  digit_of_nat ::  ‹nat ⇒ char› where
  ‹digit_of_nat x =
    (if x = 0 then CHR ''0''
    else if x = 1 then CHR ''1''
    else if x = 2 then CHR ''2''
    else if x = 3 then CHR ''3''
    else if x = 4 then CHR ''4''
    else if x = 5 then CHR ''5''
    else if x = 6 then CHR ''6''
    else if x = 7 then CHR ''7''
    else if x = 8 then CHR ''8''
    else if x = 9 then CHR ''9''
    else undefined)›

declare digit_of_nat_def [solidity_symbex]

lemma nat_of_digit_digit_of_nat_id: 
    ‹x < 10 ⟹ nat_of_digit (digit_of_nat x) = x›
  by(simp add:nat_of_digit_def digit_of_nat_def)

lemma img_digit_of_nat: 
‹n < 10 ⟹ digit_of_nat n ∈ {CHR ''0'', CHR ''1'', CHR ''2'', CHR ''3'', CHR ''4'',
                              CHR ''5'', CHR ''6'', CHR ''7'', CHR ''8'', CHR ''9''}›
  by(simp add:nat_of_digit_def digit_of_nat_def)

lemma digit_of_nat_nat_of_digit_id: 
    ‹c ∈ {CHR ''0'', CHR ''1'', CHR ''2'', CHR ''3'', CHR ''4'',
          CHR ''5'', CHR ''6'', CHR ''7'', CHR ''8'', CHR ''9''} 
      ⟹ digit_of_nat (nat_of_digit c) = c›
  by(code_simp, auto) 

definition 
  nat_implode :: ‹'a::{numeral,power,zero} list ⇒ 'a› where
 ‹nat_implode n = foldr (+) (map (λ (p,d) ⇒ 10 ^ p * d) (enumerate 0 (rev n))) 0›

declare nat_implode_def [solidity_symbex]

fun nat_explode' :: ‹nat ⇒ nat list› where 
   ‹nat_explode' x = (case  x < 10 of True ⇒ [x mod 10] 
                                       | _ ⇒ (x mod 10 )#(nat_explode' (x div 10)))›

definition 
  nat_explode :: ‹nat ⇒ nat list› where
 ‹nat_explode x = (rev (nat_explode' x))›

declare nat_explode_def [solidity_symbex]

lemma nat_explode'_not_empty: ‹nat_explode' n ≠ []› 
  by (induction n rule: nat_explode'.induct) (simp_all split: bool.split)

lemma nat_explode_not_empty: ‹nat_explode n ≠ []›
  using nat_explode'_not_empty nat_explode_def by auto 

lemma nat_explode'_ne_suc: ‹∃ n. nat_explode' (Suc n) ≠ nat_explode' n›
  by(rule exI [of _ ‹0::nat›], simp)

lemma nat_explode'_digit: ‹hd (nat_explode' n ) < 10›
proof(induct  ‹n›)
  case 0
  then show ?case by simp
next
  case (Suc n)
  then show ?case proof (cases ‹n < 9›)
    case True
    then show ?thesis by simp 
  next
    case False
    then show ?thesis 
      by simp
  qed
qed

lemma div_ten_less: ‹n ≠ 0 ⟹ ((n::nat) div 10) < n›
  by simp

lemma unroll_nat_explode': 
 ‹¬ n < 10 ⟹ (case n < 10 of True ⇒ [n mod 10] | False ⇒ n mod 10 # nat_explode' (n div 10)) =
       (n mod 10 # nat_explode' (n div 10))›
  by simp

lemma nat_explode_mod_10_ident: ‹map (λ x. x mod 10) (nat_explode' n) = nat_explode' n›
proof (cases ‹n < 10›)
  case True
  then show ?thesis by simp
next
  case False
  then show ?thesis 
  proof (induct ‹n› rule: nat_less_induct)
  case (1 n) note * = this
  then show ?case 
    using div_ten_less apply(simp (no_asm))  
    using unroll_nat_explode'[of n] * 
    by (smt (verit) list.simps(8) list.simps(9) mod_div_trivial mod_eq_self_iff_div_eq_0 
                 nat_explode'.simps zero_less_numeral)
  qed
qed

lemma nat_explode'_digits:
  ‹∀d ∈ set (nat_explode' n). d < 10›
proof
  fix d
  assume ‹d ∈ set (nat_explode' n)›
  then have ‹d ∈ set (map (λm. m mod 10) (nat_explode' n))›
    by (simp only: nat_explode_mod_10_ident)
  then show ‹d < 10›
    by auto
qed

lemma nat_explode_digits:
  ‹∀d ∈ set (nat_explode n). d < 10›
  using nat_explode'_digits [of n] by (simp only: nat_explode_def set_rev)

value ‹nat_implode(nat_explode 42) = 42›
value ‹nat_explode (Suc 21)›


lemma nat_implode_append: 
 ‹nat_implode (a@[b]) = (1*b + foldr (+) (map (λ(p, y). 10 ^ p * y) (enumerate (Suc 0) (rev a))) 0 )›
  by(simp add:nat_implode_def)

lemma enumerate_suc: ‹enumerate (Suc n) l = map (λ (a,b). (a+1::nat,b)) (enumerate n l)›
proof (induction ‹l›)
  case Nil
  then show ?case by simp
next
  case (Cons a x) note * = this
  then show ?case apply(simp only:enumerate_simps)
    
    apply(simp only:‹enumerate (Suc n) x = map (λa. case a of (a, b) ⇒ (a + 1, b)) (enumerate n x)›[symmetric])
    apply(simp)
    using *
    by (metis apfst_conv cond_case_prod_eta enumerate_Suc_eq)
qed

lemma mult_assoc_aux1: 
 ‹(λ(p, y). 10 ^ p * y) ∘ (λ(a, y). (Suc a, y)) = (λ(p, y). (10::nat) * (10 ^ p) * y)›
  by(auto simp:o_def)

lemma fold_map_transfer: 
 ‹(foldr (+) (map (λ(x,y). 10 * (f (x,y))) l) (0::nat)) = 10 * (foldr (+) (map (λx. (f x)) l) (0::nat))›
proof(induct ‹l›)            
  case Nil
  then show ?case by(simp)
next
  case (Cons a l)
  then show ?case  by(simp)
qed 

lemma mult_assoc_aux2: ‹(λ(p, y). 10 * 10 ^ p * (y::nat)) = (λ(p, y). 10 * (10 ^ p * y))›
  by(auto)

lemma nat_implode_explode_id: ‹nat_implode (nat_explode n) = n›
proof (cases ‹n=0›) 
  case True note * = this
  then show ?thesis 
    by (simp add: nat_explode_def nat_implode_def) 
next
  case False
  then show ?thesis 
  proof (induct ‹n› rule: nat_less_induct)
    case (1 n) note * = this
    then  have 
      **: ‹nat_implode (nat_explode (n div 10)) = n div 10›
    proof (cases ‹n div 10 = 0›) 
      case True 
      then show ?thesis by(simp add: nat_explode_def nat_implode_def)
    next
      case False
      then show ?thesis 
      using div_ten_less[of ‹n›] * 
      by(simp)      
  qed
    then show ?case  
    proof (cases ‹n < 10›)
      case True
      then show ?thesis  by(simp add: nat_explode_def nat_implode_def)
    next 
      case False note *** = this
      then show ?thesis 
        apply(simp (no_asm)  add:nat_explode_def del:rev_rev_ident)
        apply(simp only: bool.case(2))
        apply(simp del:nat_explode'.simps rev_rev_ident)
        apply(fold nat_explode_def)
        apply(simp only:nat_implode_append)
        apply(simp add:enumerate_suc)
        apply(simp only:mult_assoc_aux1) 
        using mult_assoc_aux2 apply(simp)
        using fold_map_transfer[of ‹λ(p, y). 10 ^ p * y› 
                                   ‹(enumerate 0 (rev (nat_explode (n div 10))))›, simplified]
        apply(simp) apply(fold nat_implode_def) using ** 
        by simp
      qed
  qed
qed

definition 
  Readnat :: ‹string ⇒ nat› where 
 ‹Readnat s = nat_implode (map nat_of_digit s)›

definition 
  Shownat::"nat ⇒ string" where
 ‹Shownat n = map digit_of_nat (nat_explode n)›

declare Readnat_def [solidity_symbex]
        Shownat_def [solidity_symbex]

definition 
  ‹STR_is_nat s = (Shownat (Readnat s) = s)›

value ‹Readnat ''10''›
value ‹Shownat 10›
value ‹Readnat (Shownat (10)) = 10›
value ‹Shownat (Readnat (''10'')) = ''10''›

lemma Show_nat_not_neg: 
  ‹set (Shownat n) ⊆{CHR ''0'', CHR ''1'', CHR ''2'', CHR ''3'', CHR ''4'',
                     CHR ''5'', CHR ''6'', CHR ''7'', CHR ''8'', CHR ''9''}›
  unfolding Shownat_def
  using  nat_explode_digits[of n]  img_digit_of_nat imageE image_set subsetI
  by (smt (verit) imageE image_set subsetI)

lemma Show_nat_not_empty: ‹(Shownat n) ≠ []›
  by (simp add: Shownat_def nat_explode_not_empty)

lemma not_hd: ‹L ≠ [] ⟹ e ∉ set(L) ⟹ hd L ≠ e›
  by auto

lemma Show_nat_not_neg'': ‹hd (Shownat n) ≠ (CHR ''-'')›
  using Show_nat_not_neg[of ‹n›]
  Show_nat_not_empty[of ‹n›] not_hd[of ‹Shownat n›]
  by auto

lemma Show_Read_nat_id: ‹STR_is_nat s ⟹ (Shownat (Readnat s) = s)›
  by(simp add:STR_is_nat_def)

lemma bar': ‹∀ d ∈ set l . d < 10 ⟹ map nat_of_digit (map digit_of_nat l) = l›
  using  nat_of_digit_digit_of_nat_id 
  by (simp add: map_idI)

lemma Read_Show_nat_id: ‹Readnat(Shownat n) = n›
  apply(unfold Readnat_def Shownat_def)
  using bar' nat_of_digit_digit_of_nat_id nat_explode_digits
  using nat_implode_explode_id 
  by presburger

definition 
  ReadLnat :: ‹String.literal ⇒ nat› (‹⌈_⌉›) where 
 ‹ReadLnat = Readnat ∘ String.explode›

definition 
  ShowLnat::‹nat ⇒ String.literal› (‹⌊_⌋›)where
 ‹ShowLnat = String.implode ∘ Shownat›

declare ReadLnat_def [solidity_symbex]
        ShowLnat_def [solidity_symbex]


definition 
  ‹strL_is_nat' s = (ShowLnat (ReadLnat s) = s)›

value ‹⌈STR ''10''⌉::nat›
value ‹ReadLnat (STR ''10'')›
value ‹⌊10::nat⌋›
value ‹ShowLnat 10›
value ‹ReadLnat (ShowLnat (10)) = 10›
value ‹ShowLnat (ReadLnat (STR ''10'')) = STR ''10''›

lemma Show_Read_nat'_id: ‹strL_is_nat' s ⟹ (ShowLnat (ReadLnat s) = s)›
  by(simp add:strL_is_nat'_def)

lemma digits_are_ascii: 
  ‹c ∈ {CHR ''0'', CHR ''1'', CHR ''2'', CHR ''3'', CHR ''4'', 
        CHR ''5'', CHR ''6'', CHR ''7'', CHR ''8'', CHR ''9''}
   ⟹ String.ascii_of c = c›
  by auto

lemma Shownat_ascii: ‹map String.ascii_of (Shownat n) = Shownat n›
  using Show_nat_not_neg digits_are_ascii
  by (meson map_idI subsetD)


lemma Read_Show_nat'_id: ‹ReadLnat(ShowLnat n) = n›
  apply(unfold ReadLnat_def ShowLnat_def, simp)
  by (simp add: Shownat_ascii  Read_Show_nat_id)


subsubsection‹Integer›
definition 
  Readint :: ‹string ⇒ int› where 
 ‹Readint x = (if hd x = (CHR ''-'') then  -(int (Readnat (tl x))) else int (Readnat x))›

definition 
  Showint::‹int ⇒ string› where
 ‹Showint i = (if i < 0 then (CHR ''-'')#(Shownat (nat (-i))) 
                        else Shownat (nat i))›

definition 
 ‹STR_is_int s = (Showint (Readint s) = s)›


declare Readint_def [solidity_symbex]
        Showint_def [solidity_symbex]

value ‹Readint (Showint   10)  =  10›
value ‹Readint (Showint (-10)) = -10›

value ‹Showint (Readint (''10''))  =  ''10''›
value ‹Showint (Readint (''-10'')) = ''-10''›

lemma Show_Read_id: ‹STR_is_int s ⟹ (Showint (Readint s) = s)›
  by(simp add:STR_is_int_def)
                          
lemma Read_Show_id: ‹Readint(Showint(x)) = x›
  apply(code_simp)
  apply(auto simp:Show_nat_not_neg Read_Show_nat_id)[1]
  apply(thin_tac ‹¬ x < 0 ›)
  using Show_nat_not_neg''
  by blast 

lemma STR_is_int_Show: ‹STR_is_int (Showint n)›
  by(simp add:STR_is_int_def Read_Show_id)

definition 
  ReadLint :: ‹String.literal ⇒ int› (‹⌈_⌉›) where 
 ‹ReadLint  = Readint ∘ String.explode› 

definition 
  ShowLint::‹int ⇒ String.literal› (‹⌊_⌋›) where
 ‹ShowLint =String.implode ∘ Showint›

definition 
 ‹strL_is_int' s = (ShowLint (ReadLint s) = s)›

declare ReadLint_def [solidity_symbex]
        ShowLint_def [solidity_symbex]

value ‹ReadLint (ShowLint   10)  =  10›
value ‹ReadLint (ShowLint (-10)) = -10›

value ‹ShowLint (ReadLint (STR ''10''))  =  STR ''10''›
value ‹ShowLint (ReadLint (STR ''-10'')) = STR ''-10''›

lemma Show_ReadL_id: ‹strL_is_int' s ⟹ (ShowLint (ReadLint s) = s)›
  by(simp add:strL_is_int'_def)

lemma Read_ShowL_id: ‹ReadLint (ShowLint x) = x›
proof(cases ‹x < 0›)
  case True
  then show ?thesis using ShowLint_def ReadLint_def Showint_def Shownat_ascii 
    by (metis (no_types, lifting) Read_Show_id String.ascii_of_Char comp_def implode.rep_eq list.simps(9)) 
next
  case False
  then show ?thesis using ShowLint_def ReadLint_def Showint_def Shownat_ascii 
    by (metis Read_Show_id String.explode_implode_eq comp_apply)
qed 

lemma STR_is_int_ShowL: ‹strL_is_int' (ShowLint n)›
  by(simp add:strL_is_int'_def Read_ShowL_id)

lemma String_Cancel: "a + (c::String.literal) = b + c ⟹ a = b"
using String.plus_literal.rep_eq
by (metis append_same_eq literal.explode_inject)

end