Theory Digits

theory Digits
  imports Complex_Main
begin

section ‹Representation of integers in different bases›

text ‹›

text ‹ First, we look at some useful lemmas for splitting sums. ›
lemma split_sum_first_elt_less: assumes "n<m" 
  shows "(i{n..<m}. f i) = f n + (i{Suc n ..<m}. f i)"
  using sum.atLeast_Suc_lessThan assms by blast

lemma split_sum_mid_less: assumes "i<(n::nat)"
  shows "(j<n. f j) = (j<i. f j) + (j=i..<n. f j)"
proof -
  have "(j<n. f j) = (j{..<i}  {i..<n}. f j)"
    using i < n by (intro sum.cong) auto
  also have " = (j<i. f j) + (j=i..<n. f j)"
    by (subst sum.union_disjoint) auto
  finally show "(j<n. f j) = (j<i. f j) + (j=i..<n. f j)" .
qed

text ‹In order to use representation of numbers in a basis base› and to calculate the conversion 
to and from integers, we introduce the following locale.›
locale digits =
  fixes base :: nat
  assumes base_pos: "base > 0"
begin

text ‹Conversion from basis base to integers: from_digits n d›

\begin{tabular}{lcp{8cm}}
  n:& nat›& length of representation in basis base\\
  d:& nat ⇒ nat›& function of digits in basis base where d i› is the $i$-th digit in basis base\\
  output:& nat›& natural number corresponding to $d(n-1) \dots d(0)$ as integer\\
\end{tabular}
›
fun from_digits :: "nat  (nat  nat)  nat" where
  "from_digits 0 d = 0"
| "from_digits (Suc n) d = d 0 + base * from_digits n (d  Suc)"

text ‹Alternative definition using sum:›
lemma from_digits_altdef: "from_digits n d = (i<n. d i * base ^ i)"
  by (induction n d rule: from_digits.induct)
     (auto simp add: sum.lessThan_Suc_shift o_def sum_distrib_left 
       sum_distrib_right mult_ac simp del: sum.lessThan_Suc)

text ‹Digit in basis base of some integer number: digit x i›

\begin{tabular}{lcp{8cm}}
  x:& nat›& integer\\
  i:& nat›& index\\
  output:& nat›& $i$-th digit of representation in basis base of $x$\\
\end{tabular}
›
fun digit :: "nat  nat  nat" where
  "digit x 0 = x mod base"
| "digit x (Suc i) = digit (x div base) i"

text ‹Alternative definition using divisor and modulo:›
lemma digit_altdef: "digit x i = (x div (base ^ i)) mod base"
  by (induction x i rule: digit.induct) (auto simp: div_mult2_eq) 

text ‹Every digit must be smaller that the base.›
lemma digit_less_base: "digit x i < base"
  using base_pos by (auto simp: digit_altdef)

text ‹A representation in basis base› of length $n$ must be less than $base› ^ n$.›
lemma from_digits_less: 
  assumes "i<n. d i < base" 
  shows "from_digits n d < base ^ n"
using assms proof (induct n d rule: from_digits.induct)
  case (2 n d)
  have "from_digits n (d  Suc)  base ^ n -1" using 2 
    by (metis One_nat_def Suc_leI Suc_pred base_pos comp_apply 
        less_Suc_eq_le zero_less_power)
  moreover have "d 0  base -1" using 2 
    by (metis One_nat_def Suc_pred base_pos less_Suc_eq_0_disj 
        less_Suc_eq_le)
  ultimately have "d 0 + base * from_digits n (d  Suc)  
      base - 1 + base * (base^(n) - 1)"
    by (simp add: add_mono_thms_linordered_semiring(1))
  then show "from_digits (Suc n) d < base ^ Suc n" 
    using base_pos by (auto simp:comp_def) 
    (metis Suc_pred add_gr_0 le_imp_less_Suc mult_Suc_right 
      zero_less_power)
qed auto

text ‹Lemmas for mod› and div› in number systems of basis base›:›
lemma mod_base:  assumes "i. i<n  d i < base" "n>0"
  shows "from_digits n d mod base = d 0 "
proof -
  have "(i<n. d i * base ^ i) mod base = 
          (i<n. d i * base ^ i mod base) mod base"  
  by (subst mod_sum_eq[symmetric]) simp
  then show ?thesis using assms 
      sum.lessThan_Suc_shift[of "(λi. d i * base ^ i mod base)" "n-1"]
    unfolding from_digits_altdef by simp
qed

lemma mod_base_i:  
  assumes "i. i<n  d i < base" "n>0" "i<n"
  shows "(j=i..<n. d j * base ^ (j-i)) mod base = d i "
proof -
  have "(j=i..<n. d j * base ^ (j-i)) mod base = 
        (j=i..<n. d j * base ^ (j-i) mod base) mod base"  
    by (subst mod_sum_eq[symmetric]) simp
  then show ?thesis 
    using assms split_sum_first_elt_less[where 
        f = "(λj. d j * base ^ (j-i) mod base)"] 
    unfolding from_digits_altdef by simp
qed

lemma div_base_i: 
  assumes "i. i<n  d i < base" "n>0" "i<n"
  shows "from_digits n d div (base ^i) = (j=i..<n. d j * base ^ (j-i))"
  unfolding from_digits_altdef proof -
  have base_exp: "base^(j) =  base^(j-i) * base^i" 
    if "j{i..<n}" for j 
    by (metis Nat.add_diff_assoc2 add_diff_cancel_right' atLeastLessThan_iff 
        power_add that)
  have first:"(j<i. d j * base ^ j)< base ^ i" 
    using assms from_digits_less[where n="i"] 
    unfolding from_digits_altdef by auto
  have "(j<n. d j * base ^ j) = 
          (j<i. d j * base ^ j) + (j=i..<n. d j * base ^ j)" 
    using assms split_sum_mid_less[where f="(λj. d j * base^j)"] by auto
  then have split_sum: "(j<n. d j * base ^ j) = 
      (j<i. d j * base ^ j) + base^i * (j=i..<n. d j * base ^ (j-i))"
    using base_exp mult.assoc sum_distrib_right 
    by (smt (z3) mult.commute sum.cong)
  then show "(i<n. d i * base ^ i) div base ^ i = 
              (j = i..<n. d j * base ^ (j - i))" 
    using first by (simp add:split_sum base_pos)
qed



text ‹Conversions are inverse to each other.›
lemma digit_from_digits:
  assumes "j. j<n  d j < base" "n>0" "i<n"
  shows   "digit (from_digits n d) i = d i"
  using assms proof (cases "i=0")
  case True
  then show ?thesis 
    by (simp add: assms(1) assms(2) digits.mod_base digits_axioms)
next
  case False
  have "from_digits n d div base^i mod base = d i" 
    using assms by (auto simp add: div_base_i mod_base_i) 
  then show "digit (from_digits n d) i = d i" 
    unfolding digit_altdef by auto
qed

lemma div_distrib: assumes "i<n" 
  shows "(a*base^n + b) div base^i mod base = b div base^i mod base"
proof -
  have "base^i dvd (a*base^n)" using assms 
    by (simp add: le_imp_power_dvd)
  moreover have "a*base^n div base^i mod base = 0" 
    by (metis Suc_leI assms dvd_imp_mod_0 dvd_mult 
        dvd_mult_imp_div le_imp_power_dvd power_Suc)
  ultimately show ?thesis
    by (metis add.right_neutral div_mult_mod_eq 
        div_plus_div_distrib_dvd_left mod_mult_self3)
qed

lemma from_digits_digit:
  assumes "x < base ^ n"
  shows   "from_digits n (digit x) = x"
  using assms unfolding digit_altdef from_digits_altdef 
proof (induction n arbitrary: x)
  case 0
  then show ?case by simp
next
  case (Suc n)
  define x_less where "x_less = x mod base^n"
  define x_n where "x_n = x div base^n"
  have "x_less < base^n" 
    using x_less_def base_pos mod_less_divisor by presburger
  then have IH_x_less:
    "(i<n. x_less div base ^ i mod base * base ^ i) = x_less" 
    using Suc.IH by simp
  have "x_n < base" using x<base^Suc n 
    by auto (metis less_mult_imp_div_less x_n_def)
  then have "x_n mod base = x_n" by simp
  have x_less_i_eq_x_i:"x mod base^n div base ^i mod base = 
    x div base^i mod base" if "i<n" for i
  proof -
    have "x div base^i mod base = 
          ((x div base^n) * base^n + x mod base^n) div base^i mod base"
      using div_mult_mod_eq[of x "base^n"] by simp
    also have " = x mod base^n div base^i mod base" 
      using div_distrib[where a="x div base^n" and b = "x mod base^n"]
        that by auto
    finally show ?thesis by simp
  qed
  have "x = (x_n mod base)*base^n + x_less" 
    unfolding x_n mod base=x_n 
    using x_n_def x_less_def div_mod_decomp by blast 
  also have " = (x div base^n mod base) * base^n + 
                (i<n. x div base ^ i mod base * base ^ i)"
    using IH_x_less x_less_def x_less_i_eq_x_i x_n_def by auto
  finally show ?case using sum.atMost_Suc 
    by (simp add: add.commute)
qed

text ‹Stronger formulation of above lemma.›
lemma from_digits_digit': 
  "from_digits n (digit x) = x mod (base ^ n)"
  unfolding from_digits_altdef digit_altdef 
proof (induction n arbitrary: x)
  case 0
  then show ?case by simp
next
  case (Suc n)
  define x_less where "x_less = x mod base^n"
  define x_n where "x_n = x div base^n mod base"
  have "x_less < base^n" using x_less_def base_pos 
      mod_less_divisor by presburger
  then have IH_x_less:
    "(i<n. x_less div base ^ i mod base * base ^ i) = x_less" 
    using Suc.IH by simp
  have "x_n < base" using base_pos mod_less_divisor x_n_def 
    by blast
  then have "x_n mod base = x_n" by simp
  have x_less_i_eq_x_i:"x mod base^n div base ^i mod base = 
    x div base^i mod base" if "i<n" for i
  proof -
    have "x div base^i mod base = 
      ((x div base^n) * base^n + x mod base^n) div base^i mod base"
      using div_mult_mod_eq[of x "base^n"] by simp
    also have " = x mod base^n div base^i mod base" 
      using div_distrib[where a="x div base^n" and b = "x mod base^n"] 
        that by auto
    finally show ?thesis by simp
  qed
  have "x mod base^Suc n = x_n*base^n + x_less" 
    by (metis mod_mult2_eq mult.commute power_Suc2 x_less_def x_n_def)
  also have " = (x div base^n mod base) * base^n + 
                (i<n. x div base ^ i mod base * base ^ i)"
    using IH_x_less x_less_def x_less_i_eq_x_i x_n_def by auto
  finally show ?case using sum.atMost_Suc 
    by (simp add: add.commute)
qed

end
end