Theory Ramanujan_Sums

(*
  File:     Ramanujan_Sums.thy
  Authors:  Rodrigo Raya, EPFL; Manuel Eberl, TUM

  Ramanujan sums and generalised Ramanujan sums
*)
section ‹Ramanujan sums›
theory Ramanujan_Sums
imports
  Dirichlet_Series.Moebius_Mu
  Gauss_Sums_Auxiliary
  Finite_Fourier_Series
begin

subsection ‹Basic sums›

definition ramanujan_sum :: "nat  nat  complex"
  where "ramanujan_sum k n = (m | m  {1..k}  coprime m k. unity_root k (m*n))"

notation ramanujan_sum (c)

lemma ramanujan_sum_0_n [simp]: "c 0 n = 0"
  unfolding ramanujan_sum_def by simp

lemma sum_coprime_conv_dirichlet_prod_moebius_mu:
  fixes F S :: "nat  complex" and f :: "nat  nat  complex"
  defines "F  (λn. (k  {1..n}. f k n))"
  defines "S  (λn. (k | k  {1..n}  coprime k n . f k n))"
  assumes "a b d. d dvd a  d dvd b  f (a div d) (b div d) = f a b" 
  shows "S n = dirichlet_prod moebius_mu F n"
proof (cases "n = 0")
  case True
  then show ?thesis 
    using assms(2) unfolding dirichlet_prod_def by fastforce
next
  case False
  have "S(n) = (k | k  {1..n}  coprime k n . (f k n))"
    using assms by blast
  also have " = (k  {1..n}. (f k n)* dirichlet_prod_neutral (gcd k n))"
    using dirichlet_prod_neutral_intro by blast
  also have " = (k  {1..n}. (f k n)* (d | d dvd (gcd k n). moebius_mu d))"
  proof -
    {
      fix k
      have "dirichlet_prod_neutral (gcd k n) = (if gcd k n = 1 then 1 else 0)"
        using dirichlet_prod_neutral_def[of "gcd k n"] by blast
      also have " = (d | d dvd gcd k n. moebius_mu d)"
        using sum_moebius_mu_divisors'[of "gcd k n"] by auto
      finally have "dirichlet_prod_neutral (gcd k n) = (d | d dvd gcd k n. moebius_mu d)" 
        by auto
    } note summand = this
    then show ?thesis by (simp add: summand)
  qed
  also have " = (k = 1..n. (d | d dvd gcd k n. (f k n) *  moebius_mu d))"
    by (simp add: sum_distrib_left)
  also have " = (k = 1..n. (d | d dvd gcd n k. (f k n) *  moebius_mu d))"
    using gcd.commute[of _ n] by simp
  also have " = (d | d dvd n. k | k  {1..n}  d dvd k. (f k n) * moebius_mu d)"
    using sum.swap_restrict[of "{1..n}" "{d. d dvd n}"
             "λk d. (f k n)*moebius_mu d" "λk d. d dvd k"] False by auto
  also have " = (d | d dvd n. moebius_mu d * (k | k  {1..n}  d dvd k. (f k n)))" 
    by (simp add: sum_distrib_left mult.commute)
  also have " = (d | d dvd n. moebius_mu d * (q  {1..n div d}. (f q (n div d))))"
  proof - 
    have st: "
      (k | k  {1..n}  d dvd k. (f k n)) =
        (q  {1..n div d}. (f q (n div d)))" 
      if "d dvd n" "d > 0" for d :: nat
      by (rule sum.reindex_bij_witness[of _ "λk. k * d" "λk. k div d"])
         (use assms(3) that in fastforce simp: div_le_mono)+
    show ?thesis 
      by (intro sum.cong) (use st False in fastforce)+
  qed
  also have " = (d | d dvd n. moebius_mu d * F(n div d))"
  proof - 
    have "F (n div d) = (q  {1..n div d}. (f q (n div d)))" 
      if "d dvd n" for d
        by (simp add: F_def real_of_nat_div that)
     then show ?thesis by auto
  qed
  also have " = dirichlet_prod moebius_mu F n"
    by (simp add: dirichlet_prod_def)
  finally show ?thesis by simp
qed

lemma dirichlet_prod_neutral_sum:
  "dirichlet_prod_neutral n = (k = 1..n. unity_root n k)" for n :: nat
proof (cases "n = 0")
  case True then show ?thesis unfolding dirichlet_prod_neutral_def by simp
next
  case False
  have 1: "unity_root n 0 = 1" by simp
  have 2: "unity_root n n = 1" 
    using unity_periodic_arithmetic[of n] add.left_neutral
  proof -
    have "1 = unity_root n (int 0)"
       using 1 by auto
    also have "unity_root n (int 0) = unity_root n (int (0 + n))"
      using unity_periodic_arithmetic[of n] periodic_arithmetic_def by algebra
    also have " = unity_root n (int n)" by simp
    finally show ?thesis by auto 
  qed
  have "(k = 1..n. unity_root n k) = (k = 0..n. unity_root n k) - 1"
    by (simp add: sum.atLeast_Suc_atMost sum.atLeast0_atMost_Suc_shift 1)
  also have " = ((k = 0..n-1. unity_root n k)+1) - 1"
    using sum.atLeast0_atMost_Suc[of "(λk. unity_root n k)" "n-1"] False 
    by (simp add: 2)
  also have " = (k = 0..n-1. unity_root n k)"
    by simp
  also have " = unity_root_sum n 1"
    unfolding unity_root_sum_def using n  0 by (intro sum.cong) auto
  also have " = dirichlet_prod_neutral n"
    using unity_root_sum[of n 1] False
    by (cases "n = 1",auto simp add: False dirichlet_prod_neutral_def)
  finally have 3: "dirichlet_prod_neutral n = (k = 1..n. unity_root n k)" by auto
  then show ?thesis by blast 
qed

lemma moebius_coprime_sum:
  "moebius_mu n = (k | k  {1..n}  coprime k n . unity_root n (int k))"
proof -
  let ?f = "(λk n. unity_root n k)"
  from div_dvd_div have " 
      d dvd a  d dvd b 
      unity_root (a div d) (b div d) = 
      unity_root a b" for a b d :: nat
    using unity_root_def real_of_nat_div by fastforce
  then have "(k | k  {1..n}  coprime k n. ?f k n) =
        dirichlet_prod moebius_mu (λn. k = 1..n. ?f k n) n"
    using sum_coprime_conv_dirichlet_prod_moebius_mu[of ?f n] by blast
  also have " = dirichlet_prod moebius_mu dirichlet_prod_neutral n"
    by (simp add: dirichlet_prod_neutral_sum)
  also have " = moebius_mu n" 
    by (cases "n = 0") (simp_all add: dirichlet_prod_neutral_right_neutral)
  finally have "moebius_mu n = (k | k  {1..n}  coprime k n. ?f k n)"
    by argo
  then show ?thesis by blast
qed

corollary ramanujan_sum_1_right [simp]: "c k (Suc 0) = moebius_mu k"
  unfolding ramanujan_sum_def using moebius_coprime_sum[of k] by simp

lemma ramanujan_sum_dvd_eq_totient:
  assumes "k dvd n"
    shows "c k n = totient k"
  unfolding ramanujan_sum_def
proof - 
  have "unity_root k (m*n) = 1" for m
    using assms by (cases "k = 0") (auto simp: unity_root_eq_1_iff_int)
  then have "(m | m  {1..k}  coprime m k. unity_root k (m * n)) = 
               (m | m  {1..k}  coprime m k. 1)" by simp
  also have " = card {m. m  {1..k}  coprime m k}" by simp
  also have " = totient k"
   unfolding totient_def totatives_def 
  proof -
    have "{1..k} = {0<..k}" by auto
    then show " of_nat (card {m  {1..k}. coprime m k}) =
              of_nat (card {ka  {0<..k}. coprime ka k})" by auto
  qed
  finally show "(m | m  {1..k}  coprime m k. unity_root k (m * n)) = totient k" 
    by auto
qed

subsection ‹Generalised sums›

definition gen_ramanujan_sum :: "(nat  complex)  (nat  complex)  nat  nat  complex" where
  "gen_ramanujan_sum f g = (λk n. d | d dvd gcd n k. f d * g (k div d))"

notation gen_ramanujan_sum (s)

lemma gen_ramanujan_sum_k_1: "s f g k 1 = f 1 * g k"
  unfolding gen_ramanujan_sum_def by auto

lemma gen_ramanujan_sum_1_n: "s f g 1 n = f 1 * g 1"
  unfolding gen_ramanujan_sum_def by simp

lemma gen_ramanujan_sum_periodic: "periodic_arithmetic (s f g k) k"
  unfolding gen_ramanujan_sum_def periodic_arithmetic_def by simp

text ‹Theorem 8.5›
theorem gen_ramanujan_sum_fourier_expansion:
  fixes f g :: "nat  complex" and a :: "nat  nat  complex"
  assumes "k > 0" 
  defines "a  (λk m. (1/k) * (d| d dvd (gcd m k). g d * f (k div d) * d))"
  shows "s f g k n = (mk-1. a k m * unity_root k (m*n))"
proof -
  let ?g = "(λx. 1 / of_nat k * (m<k. s f g k m * unity_root k (-x*m)))"
  {fix m :: nat
  let ?h = "λn d. f d * g (k div d) * unity_root k (- m * int n)"
  have "(l<k. s f g k l * unity_root k (-m*l)) =
               (l  {0..k-1}. s f g k l * unity_root k (-m*l))"
    using k > 0 by (intro sum.cong) auto
  also have " = (l  {1..k}. s f g k l * unity_root k (-m*l))"
  proof -
    have "periodic_arithmetic (λl. unity_root k (-m*l)) k"
      using unity_periodic_arithmetic_mult by blast
    then have "periodic_arithmetic (λl. s f g k l * unity_root k (-m*l)) k"
      using gen_ramanujan_sum_periodic mult_periodic_arithmetic by blast
    from this periodic_arithmetic_sum_periodic_arithmetic_shift[of _ k 1  ]
    have "sum (λl. s f g k l * unity_root k (-m*l)) {0..k - 1} = 
          sum (λl. s f g k l * unity_root k (-m*l)) {1..k}"
      using assms(1) zero_less_one by simp
    then show ?thesis by argo
  qed
  also have " = (n{1..k}. (d | d dvd (gcd n k). f(d) * g(k div d)) * unity_root k (-m*n))"
    by (simp add: gen_ramanujan_sum_def)
  also have " = (n{1..k}. (d | d dvd (gcd n k). f(d) * g(k div d) * unity_root k (-m*n)))"
    by (simp add: sum_distrib_right)
  also have " = (d | d dvd k. n | n  {1..k}  d dvd n. ?h n d)"
  proof -
    have "(n = 1..k. d | d dvd gcd n k. ?h n d) =
          (n = 1..k. d | d dvd k  d dvd n . ?h n d)"
      using gcd.commute[of _ k] by simp
    also have " = (d | d dvd k. n | n  {1..k}  d dvd n. ?h n d)"
      using sum.swap_restrict[of "{1..k}" "{d. d dvd k}"
                            _ "λn d. d dvd n"] assms by fastforce
    finally have "
      (n = 1..k. d | d dvd gcd n k. ?h n d) = 
      (d | d dvd k. n | n  {1..k}  d dvd n. ?h n d)" by blast
    then show ?thesis by simp
  qed
  also have " = (d | d dvd k. f(d)*g(k div d)*
             (n | n  {1..k}  d dvd n. unity_root k (- m * int n)))"
    by (simp add: sum_distrib_left)
  also have " = (d | d dvd k. f(d)*g(k div d)*
             (e  {1..k div d}. unity_root k (- m * (e*d))))"
    using assms(1) sum_div_reduce div_greater_zero_iff dvd_div_gt0 by auto
  also have " = (d | d dvd k. f(d)*g(k div d)*
             (e  {1..k div d}. unity_root (k div d) (- m * e)))"
  proof -
    {
      fix d e
      assume "d dvd k"
      hence "2 * pi * real_of_int (- int m * int (e * d)) / real k =
              2 * pi * real_of_int (- int m * int e) / real (k div d)" by auto
      hence "unity_root k (- m * (e * d)) = unity_root (k div d) (- m * e)"
        unfolding unity_root_def by simp
    }
    then show ?thesis by simp
  qed
  also have " = dirichlet_prod (λd. f(d)*g(k div d))
                    (λd. (e  {1..d}. unity_root d (- m * e))) k"
    unfolding dirichlet_prod_def by blast
  also have " = dirichlet_prod (λd. (e  {1..d}. unity_root d (- m * e)))
                    (λd. f(d)*g(k div d)) k"
    using dirichlet_prod_commutes[of 
            "(λd. f(d)*g(k div d))"
            "(λd. (e  {1..d}. unity_root d (- m * e)))"] by argo
  also have " = (d | d dvd k.
             (e  {1..(d::nat)}. unity_root d (- m * e))*(f(k div d)*g(k div (k div d))))"  
    unfolding dirichlet_prod_def by blast 
  also have " = (d | d dvd k. (e  {1..(d::nat)}.
                      unity_root d (- m * e))*(f(k div d)*g(d)))"  
  proof -
    {
      fix d :: nat
      assume "d dvd k"
      then have "k div (k div d) = d"
        by (simp add: assms(1) div_div_eq_right)
    }
    then show ?thesis by simp
  qed
  also have " = ((d::nat) | d dvd k  d dvd m. d*(f(k div d)*g(d)))"  
  proof -
    {
      fix d
      assume "d dvd k"
      with assms have "d > 0" by (intro Nat.gr0I) auto
      have "periodic_arithmetic (λx. unity_root d (- m * int x)) d"
        using unity_periodic_arithmetic_mult by blast
      then have "(e  {1..d}. unity_root d (- m * e)) = 
            (e  {0..d-1}. unity_root d (- m * e))"
        using periodic_arithmetic_sum_periodic_arithmetic_shift[of "λe. unity_root d (- m * e)"  d 1] assms d dvd k
        by fastforce
      also have " = unity_root_sum d (-m)" 
        unfolding unity_root_sum_def using d > 0 by (intro sum.cong) auto
      finally have 
        "(e  {1..d}. unity_root d (- m * e)) = unity_root_sum d (-m)"
        by argo
    }
    then have "
      (d | d dvd k. (e = 1..d. unity_root d (- m * int e)) * (f (k div d) * g d)) = 
      (d | d dvd k. unity_root_sum d (-m) * (f (k div d) * g d))" by simp
    also have " = (d | d dvd k  d dvd m. unity_root_sum d (-m) * (f (k div d) * g d))"
    proof (intro sum.mono_neutral_right,simp add: k > 0,blast,standard)
      fix i
      assume as: "i  {d. d dvd k} - {d. d dvd k  d dvd m}"
      then have "i  1" using k > 0 by auto
      have "k  1" using k > 0 by auto  
      have "¬ i dvd (-m)" using as by auto
      thus "unity_root_sum i (- int m) * (f (k div i) * g i) = 0" 
        using i  1 by (subst unity_root_sum(2)) auto
    qed   
    also have " = (d | d dvd k  d dvd m. d * (f (k div d) * g d))"
    proof - 
      {fix d :: nat
        assume 1: "d dvd m" 
        assume 2: "d dvd k"
        then have "unity_root_sum d (-m) = d"
          using unity_root_sum[of d "(-m)"] assms(1) 1 2
          by auto}
      then show ?thesis by auto
    qed
    finally show ?thesis by argo
  qed
  also have " = (d | d dvd gcd m k. of_nat d * (f (k div d) * g d))" 
    by (simp add: gcd.commute)
  also have " = (d | d dvd gcd m k. g d * f (k div d) * d)" 
    by (simp add: algebra_simps sum_distrib_left)
  also have "1 / k *  = a k m" using a_def by auto
  finally have "?g m = a k m" by simp}
  note a_eq_g = this
  {
    fix m
    from fourier_expansion_periodic_arithmetic(2)[of k "s f g k" ] gen_ramanujan_sum_periodic assms(1) 
    have "s f g k m = (n<k. ?g n * unity_root k (int m * n))"
      by blast
    also have " = (n<k. a k n * unity_root k (int m * n))"
      using a_eq_g by simp
    also have " = (nk-1. a k n * unity_root k (int m * n))"
      using k > 0 by (intro sum.cong) auto
    finally have "s f g k m =
      (nk - 1. a k n * unity_root k (int n * int m))"
      by (simp add: algebra_simps)
  }
  then show ?thesis by blast
qed

text ‹Theorem 8.6›
theorem ramanujan_sum_dirichlet_form:
  fixes k n :: nat
  assumes "k > 0"
  shows "c k n = (d | d dvd gcd n k. d * moebius_mu (k div d))"
proof -
  define a :: "nat  nat  complex" 
    where "a  =  (λk m.
   1 / of_nat k * (d | d dvd gcd m k. moebius_mu d * of_nat (k div d) * of_nat d))"

  {fix m
  have "a k m = (if gcd m k = 1 then 1 else 0)"
  proof -
   have "a k m = 1 / of_nat k * (d | d dvd gcd m k. moebius_mu d * of_nat (k div d) * of_nat d)"
      unfolding a_def by blast
   also have 2: " = 1 / of_nat k * (d | d dvd gcd m k. moebius_mu d * of_nat k)"
   proof -
     {fix d :: nat
     assume dvd: "d dvd gcd m k"
     have "moebius_mu d * of_nat (k div d) * of_nat d = moebius_mu d * of_nat k"
     proof -
       have "(k div d) * d = k" using dvd by auto
       then show "moebius_mu d * of_nat (k div d) * of_nat d = moebius_mu d * of_nat k"  
         by (simp add: algebra_simps,subst of_nat_mult[symmetric],simp)
     qed} note eq = this
     show ?thesis using sum.cong by (simp add: eq)
   qed

   also have 3: " = (d | d dvd gcd m k. moebius_mu d)"
     by (simp add: sum_distrib_left assms) 
   also have 4: " =  (if gcd m k = 1 then 1 else 0)"
     using sum_moebius_mu_divisors' by blast
   finally show "a k m  = (if gcd m k = 1 then 1 else 0)" 
     using coprime_def by blast
 qed} note a_expr = this

  let ?f = "(λm. (if gcd m k = 1 then 1 else 0) *
                 unity_root k (int m * n))"
  from gen_ramanujan_sum_fourier_expansion[of k id moebius_mu n] assms
  have "s (λx. of_nat (id x)) moebius_mu k n =
  (mk - 1.
      1 / of_nat k *
      (d | d dvd gcd m k.
         moebius_mu d * of_nat (k div d) * of_nat d) *
      unity_root k (int m * n))" by simp
  also have " = (mk - 1.
      a k m *
      unity_root k (int m * n))" using a_def by blast
  also have " = (mk - 1.
      (if gcd m k = 1 then 1 else 0) *
      unity_root k (int m * n))" using a_expr by auto
  also have " = (m  {1..k}.
      (if gcd m k = 1 then 1 else 0) *
      unity_root k (int m * n))"
  proof -    
    have "periodic_arithmetic (λm. (if gcd m k = 1 then 1 else 0) *
                 unity_root k (int m * n)) k"
    proof -
      have "periodic_arithmetic (λm. if gcd m k = 1 then 1 else 0) k"
        by (simp add: periodic_arithmetic_def)
      moreover have "periodic_arithmetic (λm. unity_root k (int m * n)) k" 
        using unity_periodic_arithmetic_mult[of k n]
        by (subst mult.commute,simp) 
      ultimately show "periodic_arithmetic ?f k"
        using mult_periodic_arithmetic by simp
    qed
    then have "sum ?f {0..k - 1} = sum ?f {1..k}"
      using periodic_arithmetic_sum_periodic_arithmetic_shift[of ?f k 1] by force
    then show ?thesis by (simp add: atMost_atLeast0)    
  qed  
  also have " = (m | m  {1..k}  gcd m k = 1.
                  (if gcd m k = 1 then 1 else 0) *     
                  unity_root k (int m * int n))"
    by (intro sum.mono_neutral_right,auto)
  also have " = (m | m  {1..k}  gcd m k = 1.
                  unity_root k (int m * int n))" by simp    
  also have " = (m | m  {1..k}  coprime m k.
                  unity_root k (int m * int n))"
    using coprime_iff_gcd_eq_1 by presburger
  also have " = c k n" unfolding ramanujan_sum_def by simp
  finally show ?thesis unfolding gen_ramanujan_sum_def by auto
qed

corollary ramanujan_sum_conv_gen_ramanujan_sum:
 "k > 0  c k n = s id moebius_mu k n"
  using ramanujan_sum_dirichlet_form unfolding gen_ramanujan_sum_def by simp

text ‹Theorem 8.7›
theorem gen_ramanujan_sum_distrib:
  fixes f g :: "nat  complex"
  assumes "a > 0" "b > 0" "m > 0" "k > 0" (* remove cond. on m,n *)
  assumes "coprime a k" "coprime b m" "coprime k m"
  assumes "multiplicative_function f" and 
          "multiplicative_function g"
  shows "s f g (m*k) (a*b) = s f g m a * s f g k b"
proof -
  from assms(1-6) have eq: "gcd (m*k) (a*b) = gcd a m * gcd k b"
   by (simp add: linear_gcd  gcd.commute mult.commute)
  have "s f g (m*k) (a*b) = 
        (d | d dvd gcd (m*k) (a*b). f(d) * g((m*k) div d))"
    unfolding gen_ramanujan_sum_def by (rule sum.cong, simp add: gcd.commute,blast) 
  also have " = 
     (d | d dvd gcd a m * gcd k b. f(d) * g((m*k) div d))"
    using eq by simp
  also have " = 
     ((d1,d2) | d1 dvd gcd a m   d2 dvd gcd k b. 
          f(d1*d2) * g((m*k) div (d1*d2)))" 
  proof -
    have b: "bij_betw (λ(d1, d2). d1 * d2)
   {(d1, d2). d1 dvd gcd a m  d2 dvd gcd k b}
   {d. d dvd gcd a m * gcd k b}" 
      using assms(5) reindex_product_bij by blast
    have "((d1, d2) | d1 dvd gcd a m  d2 dvd gcd k b.
     f (d1 * d2) * g (m * k div (d1 * d2))) = 
      (x{(d1, d2). d1 dvd gcd a m  d2 dvd gcd k b}.
     f (case x of (d1, d2)  d1 * d2)*
       g (m * k div (case x of (d1, d2)  d1 * d2)))"
        by (rule sum.cong,auto)
      also have " = (d | d dvd gcd a m * gcd k b. f d * g (m * k div d))"
        using b by (rule sum.reindex_bij_betw[of "λ(d1,d2). d1*d2" ])
      finally show ?thesis by argo     
    qed 
  also have " = (d1 | d1 dvd gcd a m. d2 | d2 dvd gcd k b. 
                     f (d1*d2) * g ((m*k) div (d1*d2)))"
      by (simp add: sum.cartesian_product) (rule sum.cong,auto) 
    also have " = (d1 | d1 dvd gcd a m. d2 | d2 dvd gcd k b. 
                      f d1 * f d2 * g ((m*k) div (d1*d2)))"
      using assms(5) assms(8) multiplicative_function.mult_coprime
      by (intro sum.cong refl) fastforce+
    also have " = (d1 | d1 dvd gcd a m. d2 | d2 dvd gcd k b.
                      f d1 * f d2* g (m div d1) * g (k div d2))"
    proof (intro sum.cong refl, clarify, goal_cases)
      case (1 d1 d2)
      hence "g (m * k div (d1 * d2)) = g (m div d1) * g (k div d2)" 
        using assms(7,9) multipl_div
        by (meson coprime_commute dvd_gcdD1 dvd_gcdD2)
      thus ?case by simp
    qed
    also have " = (i{d1. d1 dvd gcd a m}. j{d2. d2 dvd gcd k b}.
                      f i * g (m div i) * (f j * g (k div j)))"
      by (rule sum.cong,blast,rule sum.cong,blast,simp)   
    also have " = (d1 | d1 dvd gcd a m. f d1 * g (m div d1)) *
                      (d2 | d2 dvd gcd k b. f d2 * g (k div d2))"
      by (simp add: sum_product)
    also have " = s f g m a * s f g k b"
      unfolding gen_ramanujan_sum_def by (simp add: gcd.commute)
    finally show ?thesis by blast
qed

corollary gen_ramanujan_sum_distrib_right:
 fixes f g :: "nat  complex"
 assumes "a > 0" and "b > 0" and "m > 0" (* TODO: remove cond. on m,n *)
 assumes "coprime b m"
 assumes "multiplicative_function f" and 
         "multiplicative_function g"
 shows "s f g m (a * b) = s f g m a"
proof -
  have "s f g m (a*b) = s f g m a * s f g 1 b"
    using assms gen_ramanujan_sum_distrib[of a b m 1 f g] by simp
  also have " = s f g m a * f 1 * g 1"
    using gen_ramanujan_sum_1_n by auto
  also have " = s f g m a"
    using  assms(5-6) 
    by (simp add: multiplicative_function_def)
  finally show "s f g m (a*b) = s f g m a" by blast
qed

corollary gen_ramanujan_sum_distrib_left:
 fixes f g :: "nat  complex"
 assumes "a > 0" and "k > 0" and "m > 0" (* TODO: remove cond. on m,n *)
 assumes "coprime a k" and "coprime k m"
 assumes "multiplicative_function f" and 
         "multiplicative_function g"
 shows "s f g (m*k) a = s f g m a * g k"
proof -
  have "s f g (m*k) a = s f g m a * s f g k 1"
    using assms gen_ramanujan_sum_distrib[of a 1 m k f g] by simp
  also have " = s f g m a * f(1) * g(k)" 
    using gen_ramanujan_sum_k_1 by auto
  also have " = s f g m a *  g k" 
    using assms(6)
    by (simp add: multiplicative_function_def)
  finally show ?thesis by blast
qed

corollary ramanujan_sum_distrib:
 assumes "a > 0" and "k > 0" and "m > 0" and "b > 0" (* TODO: remove cond. on m,n *)
 assumes "coprime a k" "coprime b m" "coprime m k"
 shows "c (m*k) (a*b) = c m a * c k b"
proof -
  have "c (m*k) (a*b) = s id moebius_mu (m*k) (a*b)" 
    using ramanujan_sum_conv_gen_ramanujan_sum assms(2,3) by simp
  
  also have " = (s id moebius_mu m a) * (s id moebius_mu k b)"
    using gen_ramanujan_sum_distrib[of a b m k id moebius_mu]
          assms mult_id mult_moebius mult_of_nat         
          coprime_commute[of m k] by auto
  also have " = c m a * c k b" using ramanujan_sum_conv_gen_ramanujan_sum assms by simp
  finally show ?thesis by simp
qed

corollary ramanujan_sum_distrib_right:
 assumes "a > 0" and "k > 0" and "m > 0" and "b > 0" (* remove cond. on m,n *)
 assumes "coprime b m" 
 shows "c m (a*b) = c m a"
  using assms ramanujan_sum_conv_gen_ramanujan_sum mult_id mult_moebius 
        mult_of_nat gen_ramanujan_sum_distrib_right by auto

corollary ramanujan_sum_distrib_left:
 assumes "a > 0" "k > 0" "m > 0"  (* remove cond. on m,n *)
 assumes "coprime a k" "coprime m k" 
 shows "c (m*k) a = c m a * moebius_mu k"
  using assms
  by (simp add: ramanujan_sum_conv_gen_ramanujan_sum, subst gen_ramanujan_sum_distrib_left)
     (auto simp: coprime_commute mult_of_nat mult_moebius)

lemma dirichlet_prod_completely_multiplicative_left:
  fixes f h :: "nat  complex" and k :: nat
  defines "g  (λk. moebius_mu k * h k)" 
  defines "F  dirichlet_prod f g"
  assumes "k > 0"
  assumes "completely_multiplicative_function f" 
          "multiplicative_function h" 
  assumes "p. prime p  f(p)  0  f(p)  h(p)" 
  shows "F k = f k * (pprime_factors k. 1 - h p / f p)"
proof -
  have 1: "multiplicative_function (λp. h(p) div f(p))"
    using multiplicative_function_divide
          comp_to_mult assms(4,5) by blast
  have "F k = dirichlet_prod g f k"
    unfolding F_def using dirichlet_prod_commutes[of f g] by auto
  also have " = (d | d dvd k. moebius_mu d * h d * f(k div d))"
    unfolding g_def dirichlet_prod_def by blast
  also have " = (d | d dvd k. moebius_mu d * h d * (f(k) div f(d)))"
    using multipl_div_mono[of f _ k] assms(4,6) 
    by (intro sum.cong,auto,force)  
  also have " = f k * (d | d dvd k. moebius_mu d * (h d div f(d)))"
    by (simp add: sum_distrib_left algebra_simps)
  also have " = f k * (pprime_factors k. 1 - (h p div f p))"
    using sum_divisors_moebius_mu_times_multiplicative[of "λp. h p div f p" k] 1
          assms(3) by simp
  finally show F_eq: "F k = f k * (pprime_factors k. 1 - (h p div f p))"
    by blast
qed

text ‹Theorem 8.8›
theorem gen_ramanujan_sum_dirichlet_expr:
  fixes f h :: "nat  complex" and n k :: nat
  defines "g  (λk. moebius_mu k * h k)" 
  defines "F  dirichlet_prod f g"
  defines "N  k div gcd n k" 
  assumes "completely_multiplicative_function f" 
          "multiplicative_function h" 
  assumes "p. prime p  f(p)  0  f(p)  h(p)" 
  assumes "k > 0" "n > 0"  
  shows "s f g k n = (F(k)*g(N)) div (F(N))"
proof -
  define a where "a  gcd n k" 
  have 2: "k = a*N" unfolding a_def N_def by auto
  have 3: "a > 0" using a_def assms(7,8) by simp
  have Ngr0: "N > 0" using assms(7,8) 2 N_def by fastforce
  have f_k_not_z: "f k  0" 
    using completely_multiplicative_nonzero assms(4,6,7) by blast
  have f_N_not_z: "f N  0" 
      using completely_multiplicative_nonzero assms(4,6) Ngr0 by blast
  have bij: "bij_betw (λd. a div d) {d. d dvd a} {d. d dvd a}"
    unfolding bij_betw_def
  proof
    show inj: "inj_on (λd. a div d) {d. d dvd a}"
      using inj_on_def "3" dvd_div_eq_2 by blast
    show surj: "(λd. a div d) ` {d. d dvd a} = {d. d dvd a}"
      unfolding image_def 
    proof 
      show " {y. x{d. d dvd a}. y = a div x}  {d. d dvd a}"
        by auto
      show "{d. d dvd a}  {y. x{d. d dvd a}. y = a div x}"
      proof 
        fix d
        assume a: "d  {d. d dvd a}"
        from a have 1: "(a div d)  {d. d dvd a}" by auto
        from a have 2: "d = a div (a div d)" using 3 by auto
        from 1 2 show "d  {y. x{d. d dvd a}. y = a div x} " by blast        
      qed
    qed
  qed
  
  have "s f g k n = (d | d dvd a. f(d)*moebius_mu(k div d)*h(k div d))"
    unfolding gen_ramanujan_sum_def g_def a_def by (simp add: mult.assoc)
  also have " = (d | d dvd a. f(d) * moebius_mu(a*N div d)*h(a*N div d))"
    using 2 by blast
  also have " = (d | d dvd a. f(a div d) * moebius_mu(N*d)*h(N*d))"
    (is "?a = ?b")
  proof -
    define f_aux where "f_aux  (λd. f d * moebius_mu (a * N div d) * h (a * N div d))"
    have 1: "?a = (d | d dvd a. f_aux d)" using f_aux_def by blast
    {fix d :: nat
    assume "d dvd a"
    then have "N * a div (a div d) = N * d" 
      using 3 by force}
    then have 2: "?b = (d | d dvd a. f_aux (a div d))" 
      unfolding f_aux_def by (simp add: algebra_simps)
    show "?a = ?b" 
      using bij 1 2
      by (simp add: sum.reindex_bij_betw[of "((div) a)" "{d. d dvd a}" "{d. d dvd a}"])
  qed
  also have " = moebius_mu N * h N * f a * (d | d dvd a  coprime N d. moebius_mu d * (h d div f d))"
   (is "?a = ?b")
  proof -
    have "?a = (d | d dvd a  coprime N d. f(a div d) * moebius_mu (N*d) * h (N*d))"
      by (rule sum.mono_neutral_right)(auto simp add: moebius_prod_not_coprime 3)
    also have " = (d | d dvd a  coprime N d. moebius_mu N * h N * f(a div d) * moebius_mu d * h d)"
    proof (rule sum.cong,simp)
      fix d
      assume a: "d  {d. d dvd a  coprime N d}"
      then have 1: "moebius_mu (N*d) = moebius_mu N * moebius_mu d"
        using mult_moebius unfolding multiplicative_function_def 
        by (simp add: moebius_mu.mult_coprime)
      from a have 2: "h (N*d) = h N * h d"
         using assms(5) unfolding multiplicative_function_def 
         by (simp add: assms(5) multiplicative_function.mult_coprime)
      show "f (a div d) * moebius_mu (N * d) * h (N * d) =
         moebius_mu N * h N * f (a div d) * moebius_mu d * h d"
       by (simp add: divide_simps 1 2)
    qed
    also have " = (d | d dvd a  coprime N d. moebius_mu N * h N * (f a div f d) * moebius_mu d * h d)"
      by (intro sum.cong refl) (use multipl_div_mono[of f _ a] assms(4,6-8) 3 in force)
    also have " = moebius_mu N * h N * f a * (d | d dvd a  coprime N d. moebius_mu d * (h d div f d))"
      by (simp add: sum_distrib_left algebra_simps)
    finally show ?thesis by blast
  qed
  also have " =
           moebius_mu N * h N * f a * (p{p. p  prime_factors a  ¬ (p dvd N)}. 1 - (h p div f p))"
   proof -
     have "multiplicative_function (λd. h d div f d)" 
       using multiplicative_function_divide 
             comp_to_mult 
             assms(4,5) by blast
     then have "(d | d dvd a  coprime N d. moebius_mu d * (h d div f d)) =
    (p{p. p  prime_factors a  ¬ (p dvd N)}. 1 - (h p div f p))"
       using sum_divisors_moebius_mu_times_multiplicative_revisited[
         of "(λd. h d div f d)" a N]         
           assms(8) Ngr0 3 by blast 
    then show ?thesis by argo
  qed    
  also have " = f(a) * moebius_mu(N) * h(N) * 
     ((p{p. p  prime_factors (a*N)}. 1 - (h p div f p)) div
     (p{p. p  prime_factors N}. 1 - (h p div f p)))"
  proof -
    have "{p. p prime_factors a  ¬ p dvd N} = 
          ({p. p prime_factors (a*N)} - {p. p prime_factors N})"
      using p_div_set[of a N] by blast
    then have eq2: "(p{p. p prime_factors a  ¬ p dvd N}. 1 - h p / f p) = 
          prod (λp. 1 - h p / f p) ({p. p prime_factors (a*N)} - {p. p prime_factors N})"
      by auto
    also have eq: " = prod (λp. 1 - h p / f p) {p. p prime_factors (a*N)} div
                     prod (λp. 1 - h p / f p) {p. p prime_factors N}"
    proof (intro prod_div_sub,simp,simp,simp add: "3" Ngr0 dvd_prime_factors,simp,standard)
      fix b
      assume "b ∈# prime_factorization N"
      then have p_b: "prime b" using in_prime_factors_iff by blast
      then show "f b = 0  h b  f b" using assms(6)[OF p_b] by auto
    qed
    also have " = (p{p. p  prime_factors (a*N)}. 1 - (h p div f p)) div
     (p{p. p  prime_factors N}. 1 - (h p div f p))" by blast
    finally have "(p{p. p prime_factors a  ¬ p dvd N}. 1 - h p / f p) = 
        (p{p. p  prime_factors (a*N)}. 1 - (h p div f p)) div
     (p{p. p  prime_factors N}. 1 - (h p div f p))" 
      using eq eq2 by auto
    then show ?thesis by simp
  qed
  also have " = f(a) * moebius_mu(N) * h(N) * (F(k) div f(k)) * (f(N) div F(N))"
   (is "?a = ?b")
  proof -
    have "F(N) = (f N) *(p prime_factors N. 1 - (h p div f p))"
      unfolding F_def g_def
      by (intro dirichlet_prod_completely_multiplicative_left) (auto simp add: Ngr0 assms(4-6))
    then have eq_1: "(p prime_factors N. 1 - (h p div f p)) = 
               F N div f N" using 2 f_N_not_z by simp
    have "F(k) = (f k) * (p prime_factors k. 1 - (h p div f p))"
      unfolding F_def g_def
      by (intro dirichlet_prod_completely_multiplicative_left) (auto simp add: assms(4-7))
    then have eq_2: "(p prime_factors k. 1 - (h p div f p)) = 
               F k div f k" using 2 f_k_not_z by simp

    have "?a = f a * moebius_mu N * h N * 
           ((p prime_factors k. 1 - (h p div f p)) div
           (p prime_factors N. 1 - (h p div f p)))"
      using 2 by (simp add: algebra_simps) 
    also have  " = f a * moebius_mu N * h N * ((F k div f k) div (F N div f N))"
      by (simp add: eq_1 eq_2)
    finally show ?thesis by simp
  qed
  also have " = moebius_mu N * h N * ((F k * f a * f N) div (F N * f k))"
    by (simp add: algebra_simps) 
  also have " = moebius_mu N * h N * ((F k * f(a*N)) div (F N * f k))"
  proof -
    have "f a * f N = f (a*N)" 
    proof (cases "a = 1  N = 1")
      case True
      then show ?thesis  
        using assms(4) completely_multiplicative_function_def[of f] 
        by auto
    next
      case False
      then show ?thesis 
        using 2 assms(4) completely_multiplicative_function_def[of f] 
             Ngr0 3 by auto
    qed
    then show ?thesis by simp
  qed 
  also have " = moebius_mu N * h N * ((F k * f(k)) div (F N * f k))"
    using 2 by blast
  also have " = g(N) * (F k div F N)"
    using f_k_not_z g_def by simp
  also have " = (F(k)*g(N)) div (F(N))" by auto
  finally show ?thesis by simp
qed

(*TODO remove this and substitute 
 the theorem totient_conv_moebius_mu in More_totient by 
 this version: int → of_nat*)
lemma totient_conv_moebius_mu_of_nat:
  "of_nat (totient n) = dirichlet_prod moebius_mu of_nat n"
proof (cases "n = 0")
  case False
  show ?thesis
    by (rule moebius_inversion)
       (insert False, simp_all add: of_nat_sum [symmetric] totient_divisor_sum del: of_nat_sum)
qed simp_all

corollary ramanujan_sum_k_n_dirichlet_expr:
 fixes k n :: nat
 assumes "k > 0" "n > 0" 
 shows "c k n = of_nat (totient k) * 
                moebius_mu (k div gcd n k) div 
                of_nat (totient (k div gcd n k))" 
proof -
  define f :: "nat  complex" 
    where "f  of_nat"
  define F :: "nat  complex"
    where "F  (λd. dirichlet_prod f moebius_mu d)"
  define g :: "nat  complex "
    where "g  (λl. moebius_mu l)" 
  define N where "N  k div gcd n k" 
  define h :: "nat  complex"
    where "h  (λx. (if x = 0 then 0 else 1))" 
  
  have F_is_totient_k: "F k = totient k"
    by (simp add: F_def f_def dirichlet_prod_commutes totient_conv_moebius_mu_of_nat[of k])
  have F_is_totient_N: "F N = totient N"
    by (simp add: F_def f_def dirichlet_prod_commutes totient_conv_moebius_mu_of_nat[of N])

  have "c k n = s id moebius_mu k n"
    using ramanujan_sum_conv_gen_ramanujan_sum assms by blast
  also have " =  s f g k n" 
    unfolding f_def g_def by auto
  also have "g = (λk. moebius_mu k * h k)"
    by (simp add: fun_eq_iff h_def g_def)
  also have "multiplicative_function h"
    unfolding h_def by standard auto
  hence "s f (λk. moebius_mu k * h k) k n =
           dirichlet_prod of_nat (λk. moebius_mu k * h k) k *
           (moebius_mu (k div gcd n k) * h (k div gcd n k)) /
           dirichlet_prod of_nat (λk. moebius_mu k * h k) (k div gcd n k)" 
    unfolding f_def using assms mult_of_nat_c
    by (intro gen_ramanujan_sum_dirichlet_expr) (auto simp: h_def)
  also have " = of_nat (totient k) * moebius_mu (k div gcd n k) / of_nat (totient (k div gcd n k))"
    using F_is_totient_k F_is_totient_N by (auto simp: h_def F_def N_def f_def)
  finally show ?thesis .
qed

no_notation ramanujan_sum (c)
no_notation gen_ramanujan_sum (s)

end