Theory Word_Upto

section‹Word Upto›
theory Word_Upto
imports Main
  IP_Addresses.Hs_Compat
  Word_Lib.Word_Lemmas
begin

definition word_range :: ‹'a::len word ⇒ 'a word ⇒ 'a word list›
  where ‹word_range v w = sorted_list_of_set {v..w}›

lemma word_range_code [code]:
  ‹word_range v w = (if v = w then [v] else if v < w then v # word_range (v + 1) w else [])›
proof (cases ‹v < w›)
  case True
  then have ‹{v..w} = insert v {v + 1..w}›
    by (auto simp add: eq_neg_iff_add_eq_0 less_is_non_zero_p1 word_Suc_le)
  moreover have ‹v ∉ {v + 1..w}›
    using True by (auto simp add: plus_1_less)
  ultimately show ?thesis
    using True by (auto simp add: word_range_def plus_1_less intro!: insort_is_Cons)
next
  case False
  then show ?thesis
    by (simp add: word_range_def)
qed

lemma word_interval_eq_set_word_range [code_unfold]:
  ‹{v..w} = set (word_range v w)›
  by (simp add: word_range_def)


text‹Enumerate a range of machine words.›

text‹enumerate from the back (inefficient)›
function word_upto :: "'a word ⇒ 'a word ⇒ ('a::len) word list" where
"word_upto a b = (if a = b then [a] else word_upto a (b - 1) @ [b])"
by pat_completeness auto

(*by the way: does not terminate practically if b < a; will terminate after it
  reaches the word wrap-around!*)

termination word_upto
apply(relation "measure (unat ∘ uncurry (-) ∘ prod.swap)")
 apply(rule wf_measure; fail)
apply(simp)
apply(subgoal_tac "unat (b - a - 1) < unat (b - a)")
 apply(simp add: diff_right_commute; fail)
apply(rule measure_unat)
apply auto
done

declare word_upto.simps[simp del]
 

text‹enumerate from the front (more inefficient)›
function word_upto' :: "'a word ⇒ 'a word ⇒ ('a::len) word list" where
"word_upto' a b = (if a = b then [a] else a # word_upto' (a + 1) b)"
by pat_completeness auto

termination word_upto'
apply(relation "measure (λ (a, b). unat (b - a))")
 apply(rule wf_measure; fail)
apply(simp)
apply(subgoal_tac "unat (b - a - 1) < unat (b - a)")
 apply (simp add: diff_diff_add; fail)
apply(rule measure_unat)
apply auto
done

declare word_upto'.simps[simp del]

lemma word_upto_cons_front[code]:
 "word_upto a b = word_upto' a b"
 proof(induction a b rule:word_upto'.induct)
 case (1 a b)
   have hlp1: "a ≠ b ⟹ a # word_upto (a + 1) b = word_upto a b"
   apply(induction a b rule:word_upto.induct)
   apply simp
   apply(subst(1) word_upto.simps)
   apply(simp)
   apply safe
    apply(subst(1) word_upto.simps)
    apply (simp)
    apply(subst(1) word_upto.simps)
    apply (simp; fail)
   apply(case_tac "a ≠ b - 1")
    apply(simp)
    apply (metis Cons_eq_appendI word_upto.simps)
   apply(simp)
   done

   from 1[symmetric] show ?case
     apply(cases "a = b")
      subgoal
      apply(subst word_upto.simps)
      apply(subst word_upto'.simps)
      by(simp)
     apply(subst word_upto'.simps)
     by(simp add: hlp1)
 qed


(* Most of the lemmas I show about word_upto hold without a ≤ b,
   but I don't need that right now and it's giving me a headache *)


lemma word_upto_set_eq: "a ≤ b ⟹ x ∈ set (word_upto a b) ⟷ a ≤ x ∧ x ≤ b"
proof
  show "a ≤ b ⟹ x ∈ set (word_upto a b) ⟹ a ≤ x ∧ x ≤ b"
    apply(induction a b rule: word_upto.induct)
    apply(case_tac "a = b")
     apply(subst(asm) word_upto.simps)
     apply(simp; fail)
    apply(subst(asm) word_upto.simps)
    apply(simp)
    apply(erule disjE)
     apply(simp; fail)
    proof(goal_cases)
     case (1 a b)
     from 1(2-3) have "b ≠ 0" by force
     from 1(2,3) have "a ≤ b - 1"
       by (simp add: word_le_minus_one_leq)
     from 1(1)[OF this 1(4)] show ?case by (metis dual_order.trans 1(2,3) less_imp_le measure_unat word_le_0_iff word_le_nat_alt)
    qed
next
  show "a ≤ x ∧ x ≤ b ⟹ x ∈ set (word_upto a b)"
    apply(induction a b rule: word_upto.induct)
    apply(case_tac "a = b")
     apply(subst word_upto.simps)
     apply(simp; force)
    apply(subst word_upto.simps)
    apply(simp)
    apply(case_tac "x = b")
     apply(simp;fail)
    proof(goal_cases)
       case (1 a b)
       from 1(2-4) have "b ≠ 0" by force
       from 1(2,4) have "x ≤ b - 1"
         using le_step_down_word by auto
       from 1(1) this show ?case by simp
    qed
qed

lemma word_upto_distinct_hlp: "a ≤ b ⟹ a ≠ b ⟹ b ∉ set (word_upto a (b - 1))"
   apply(rule ccontr, unfold not_not)
   apply(subgoal_tac "a ≤ b - 1")
    apply(drule iffD1[OF word_upto_set_eq[of a "b -1" b]])
     apply(simp add: word_upto.simps)
    apply(subgoal_tac "b ≠ 0")
     apply(meson leD measure_unat word_le_nat_alt)
   apply(blast intro: iffD1[OF word_le_0_iff])
  using le_step_down_word apply blast
done

lemma distinct_word_upto: "a ≤ b ⟹ distinct (word_upto a b)"
apply(induction a b rule: word_upto.induct)
apply(case_tac "a = b")
 apply(subst word_upto.simps)
 apply(simp; force)
apply(subst word_upto.simps)
apply(case_tac "a ≤ b - 1")
 apply(simp)
 apply(rule word_upto_distinct_hlp; simp)
apply(simp)
  apply(rule ccontr)
  apply (simp add: not_le antisym word_minus_one_le_leq)
done


lemma word_upto_eq_upto: "s ≤ e ⟹ e ≤ unat (max_word :: 'l word) ⟹
       word_upto ((of_nat :: nat ⇒ ('l :: len) word) s) (of_nat e) = map of_nat (upt s (Suc e))"
proof(induction e)
  let ?mwon = "of_nat :: nat ⇒ 'l word"
  let ?mmw = "max_word :: 'l word"
  case (Suc e)
  show ?case
  proof(cases "?mwon s = ?mwon (Suc e)")
    case True
    have "s = Suc e" using le_unat_uoi Suc.prems True by metis
    with True show ?thesis by(subst word_upto.simps) (simp)
  next
    case False
    hence le: "s ≤ e" using le_SucE Suc.prems by blast
    have lm: "e ≤ unat ?mmw" using Suc.prems by simp
    have sucm: "(of_nat :: nat ⇒ ('l :: len) word) (Suc e) - 1 = of_nat e" using Suc.prems(2) by simp
    note mIH = Suc.IH[OF le lm]
    show ?thesis by(subst word_upto.simps) (simp add: False[simplified] Suc.prems mIH sucm)
  qed
qed(simp add: word_upto.simps)

lemma word_upto_alt: "(a :: ('l :: len) word) ≤ b ⟹
  word_upto a b = map of_nat (upt (unat a) (Suc (unat b)))"
proof -
   let ?mmw = "- 1 :: 'l word"
   assume le: "a ≤ b"
   hence nle: "unat a ≤ unat b" by(unat_arith)
   have lem: "unat b ≤ unat ?mmw" by (simp add: word_unat_less_le) 
   then show "word_upto a b = map of_nat [unat a..<Suc (unat b)]"
   using word_upto_eq_upto [OF nle lem] by simp
qed

lemma word_upto_upt:
  "word_upto a b = (if a ≤ b then map of_nat (upt (unat a) (Suc (unat b))) else word_upto a b)"
  using word_upto_alt by metis

lemma sorted_word_upto:
  fixes a b :: "('l :: len) word"
  assumes "a ≤ b"
  shows "sorted (word_upto a b)"
proof -
  define m and n where ‹m = unat a› and ‹n = Suc (unat b)›
  moreover have ‹sorted_wrt (λx y. (word_of_nat x :: 'l word) ≤ word_of_nat y) [m..<n]›
  proof (rule sorted_wrt_mono_rel [of _ ‹(≤)›])
    show ‹sorted [m..<n]›
      by simp
    fix r s
    assume ‹r ∈ set [m..<n]› ‹s ∈ set [m..<n]› ‹r ≤ s›
    then have ‹m ≤ r› ‹s < n›
      by simp_all
    then have ‹take_bit LENGTH('l) s = s›
      by (auto simp add: m_def n_def less_Suc_eq_le unsigned_of_nat dest: le_unat_uoi)
    with ‹r ≤ s› show ‹(word_of_nat r :: 'l word) ≤ word_of_nat s›
      apply (simp add: of_nat_word_less_eq_iff)
      using take_bit_nat_less_eq_self apply (rule order_trans)
      apply assumption
      done
  qed
  then have ‹sorted (map word_of_nat [m..<n] :: 'l word list)›
    by (simp add: sorted_map)
  ultimately have ‹sorted (map of_nat [unat a..<Suc (unat b)] :: 'l word list)›
    by simp
  with assms show ?thesis
    by (simp only: word_upto_alt)
qed

end