Theory Combinatorial_Q_Analogues.Q_Pochhammer_Infinite

section ‹The infinite $q$-Pochhammer symbol $(a; q)_\infty$›
theory Q_Pochhammer_Infinite
imports
  More_Infinite_Products
  Q_Analogues
begin

subsection ‹Definition and basic properties›

definition qpochhammer_inf :: "'a :: {real_normed_field, banach, heine_borel}  'a  'a" where
  "qpochhammer_inf a q = prodinf (λk. 1 - a * q ^ k)"

bundle qpochhammer_inf_notation
begin
notation qpochhammer_inf ("'(_ ; _')")
end

bundle no_qpochhammer_inf_notation
begin
no_notation qpochhammer_inf ("'(_ ; _')")
end


lemma qpochhammer_inf_0_left [simp]: "qpochhammer_inf 0 q = 1"
  by (simp add: qpochhammer_inf_def)

lemma qpochhammer_inf_0_right [simp]: "qpochhammer_inf a 0 = 1 - a"
proof -
  have "qpochhammer_inf a 0 = (k0. 1 - a * 0 ^ k)"
    unfolding qpochhammer_inf_def by (rule prodinf_finite) auto
  also have " = 1 - a"
    by simp
  finally show ?thesis .
qed

lemma abs_convergent_qpochhammer_inf:
  fixes a q :: "'a :: {real_normed_div_algebra, banach}"
  assumes "norm q < 1"
  shows   "abs_convergent_prod (λn. 1 - a * q ^ n)"
proof (rule summable_imp_abs_convergent_prod)
  show "summable (λn. norm (1 - a * q ^ n - 1))"
    using assms by (auto simp: norm_power norm_mult)
qed

lemma convergent_qpochhammer_inf:
  fixes a q :: "'a :: {real_normed_field, banach}"
  assumes "norm q < 1"
  shows   "convergent_prod (λn. 1 - a * q ^ n)"
  using abs_convergent_qpochhammer_inf[OF assms] abs_convergent_prod_imp_convergent_prod by blast

lemma has_prod_qpochhammer_inf:
  "norm q < 1  (λn. 1 - a * q ^ n) has_prod qpochhammer_inf a q"
  using convergent_qpochhammer_inf unfolding qpochhammer_inf_def
  by (intro convergent_prod_has_prod)

text ‹
  We now also see that the infinite $q$-Pochhammer symbol $(a; q)_\infty$ really is
  the limit of $(a; q)_n$ for $n\to\infty$:
›
lemma qpochhammer_tendsto_qpochhammer_inf:
  assumes q: "norm q < 1"
  shows   "(λn. qpochhammer (int n) t q)  qpochhammer_inf t q"
  using has_prod_imp_tendsto'[OF has_prod_qpochhammer_inf[OF q, of t]]
  by (simp add: qpochhammer_def)

lemma qpochhammer_inf_of_real:
  assumes "¦q¦ < 1"
  shows   "qpochhammer_inf (of_real a) (of_real q) = of_real (qpochhammer_inf a q)"
proof -
  have "(λn. of_real (1 - a * q ^ n) :: 'a) has_prod of_real (qpochhammer_inf a q)"
    unfolding has_prod_of_real_iff by (rule has_prod_qpochhammer_inf) (use assms in auto)
  also have "(λn. of_real (1 - a * q ^ n) :: 'a) = (λn. 1 - of_real a * of_real q ^ n)"
    by simp
  finally have " has_prod of_real (qpochhammer_inf a q)" .
  moreover have "(λn. 1 - of_real a * of_real q ^ n :: 'a) has_prod 
                   qpochhammer_inf (of_real a) (of_real q)"
    by (rule has_prod_qpochhammer_inf) (use assms in auto)
  ultimately show ?thesis
    using has_prod_unique2 by blast
qed

lemma qpochhammer_inf_zero_iff:
  assumes q: "norm q < 1"
  shows   "qpochhammer_inf a q = 0  (n. a * q ^ n = 1)"
proof -
  have "(λn. 1 - a * q ^ n) has_prod qpochhammer_inf a q"
    using has_prod_qpochhammer_inf[OF q] by simp
  hence "qpochhammer_inf a q = 0  (n. a * q ^ n = 1)"
    by (subst has_prod_eq_0_iff) auto
  thus ?thesis .
qed

lemma qpochhammer_inf_nonzero:
  assumes "norm q < 1" "norm a < 1"
  shows   "qpochhammer_inf a q  0"
proof
  assume "qpochhammer_inf a q = 0"
  then obtain n where n: "a * q ^ n = 1"
    using assms by (subst (asm) qpochhammer_inf_zero_iff) auto
  have "norm (q ^ n) * norm a  1 * norm a"
    unfolding norm_power using assms by (intro mult_right_mono power_le_one) auto
  also have "  < 1"
    using assms by simp
  finally have "norm (a * q ^ n) < 1"
    by (simp add: norm_mult mult.commute)
  with n show False
    by auto
qed


lemma qpochhammer_inf_pos:
  assumes "¦q¦ < 1" "¦a¦ < (1::real)"
  shows   "qpochhammer_inf a q > 0"
  using has_prod_qpochhammer_inf
proof (rule has_prod_pos)
  fix n :: nat
  have "¦a * q ^ n¦ = ¦a¦ * ¦q¦ ^ n"
    by (simp add: abs_mult power_abs)
  also have "¦a¦ * ¦q¦ ^ n  ¦a¦ * 1 ^ n"
    by (intro mult_left_mono power_mono) (use assms in auto)
  also have " < 1"
    using assms by simp
  finally show "0 < 1 - a * q ^ n"
    by simp
qed (use assms in auto)

lemma qpochhammer_inf_nonneg:
  assumes "¦q¦ < 1" "¦a¦  (1::real)"
  shows   "qpochhammer_inf a q  0"
  using has_prod_qpochhammer_inf
proof (rule has_prod_nonneg)
  fix n :: nat
  have "¦a * q ^ n¦ = ¦a¦ * ¦q¦ ^ n"
    by (simp add: abs_mult power_abs)
  also have "¦a¦ * ¦q¦ ^ n  ¦a¦ * 1 ^ n"
    by (intro mult_left_mono power_mono) (use assms in auto)
  also have " 1"
    using assms by simp
  finally show "0  1 - a * q ^ n"
    by simp
qed (use assms in auto)


subsection ‹Uniform convergence and its consequences›

context
  fixes P :: "nat  'a :: {real_normed_field, banach, heine_borel}  'a  'a"
  defines "P  (λN a q. n<N. 1 - a * q ^ n)"
begin 

lemma uniformly_convergent_qpochhammer_inf_aux:
  assumes r: "0  ra" "0  rq" "rq < 1"
  shows   "uniformly_convergent_on (cball 0 ra × cball 0 rq) (λn (a,q). P n a q)"
  unfolding P_def case_prod_unfold
proof (rule uniformly_convergent_on_prod')
  show "uniformly_convergent_on (cball 0 ra × cball 0 rq)
          (λN aq. n<N. norm (1 - fst aq * snd aq ^ n - 1 :: 'a))"
  proof (intro Weierstrass_m_test'_ev always_eventually allI ballI)
    show "summable (λn. ra * rq ^ n)" using r
      by (intro summable_mult summable_geometric) auto
  next
    fix n :: nat and aq :: "'a × 'a"
    assume "aq  cball 0 ra × cball 0 rq"
    then obtain a q where [simp]: "aq = (a, q)" and aq: "norm a  ra" "norm q  rq"
      by (cases aq) auto
    have "norm (norm (1 - a * q ^ n - 1)) = norm a * norm q ^ n"
      by (simp add: norm_mult norm_power)
    also have "  ra * rq ^ n"
      using aq r by (intro mult_mono power_mono) auto
    finally show "norm (norm (1 - fst aq * snd aq ^ n - 1))  ra * rq ^ n"
      by simp
  qed
qed (auto intro!: continuous_intros compact_Times) 

lemma uniformly_convergent_qpochhammer_inf:
  assumes "compact A" "A  UNIV × ball 0 1"
  shows   "uniformly_convergent_on A (λn (a,q). P n a q)"
proof (cases "A = {}")
  case False
  obtain rq where rq: "rq  0" "rq < 1" "a q. (a, q)  A  norm q  rq"
  proof -
    from compact A have "compact (norm ` snd ` A)"
      by (intro compact_continuous_image continuous_intros)
    hence "Sup (norm ` snd ` A)  norm ` snd ` A"
      by (intro closed_contains_Sup bounded_imp_bdd_above compact_imp_bounded compact_imp_closed)
         (use A  {} in auto)
    then obtain aq0 where aq0: "aq0  A" "norm (snd aq0) = Sup (norm ` snd ` A)"
      by auto
    show ?thesis
    proof (rule that[of "norm (snd aq0)"])
      show "norm (snd aq0)  0" and "norm (snd aq0) < 1"
        using assms(2) aq0(1) by auto
    next
      fix a q assume "(a, q)  A"
      hence "norm q  Sup (norm ` snd ` A)"
        by (intro cSup_upper bounded_imp_bdd_above compact_imp_bounded assms
              compact_continuous_image continuous_intros) force
      with aq0 show "norm q  norm (snd aq0)"
        by simp
    qed
  qed

  obtain ra where ra: "ra  0" "a q. (a, q)  A  norm a  ra"
  proof -
    have "bounded (fst ` A)"
      by (intro compact_imp_bounded compact_continuous_image continuous_intros assms)
    then obtain ra where ra: "norm a  ra" if "a  fst ` A" for a
      unfolding bounded_iff by blast
    from A  {} obtain aq0 where "aq0  A"
      by blast
    have "0  norm (fst aq0)"
      by simp
    also have "fst aq0  fst ` A"
      using aq0  A by blast
    with ra[of "fst aq0"] and A  {} have "norm (fst aq0)  ra"
      by simp
    finally show ?thesis
      using that[of ra] ra by fastforce
  qed

  have "uniformly_convergent_on (cball 0 ra × cball 0 rq) (λn (a,q). P n a q)"
    by (intro uniformly_convergent_qpochhammer_inf_aux) (use ra rq in auto)
  thus ?thesis
    by (rule uniformly_convergent_on_subset) (use ra rq in auto)
qed auto

lemma uniform_limit_qpochhammer_inf:
  assumes "compact A" "A  UNIV × ball 0 1"
  shows   "uniform_limit A (λn (a,q). P n a q) (λ(a,q). qpochhammer_inf a q) at_top"
proof -
  obtain g where g: "uniform_limit A (λn (a,q). P n a q) g at_top"
    using uniformly_convergent_qpochhammer_inf[OF assms(1,2)]
    by (auto simp: uniformly_convergent_on_def)
  also have "?this  uniform_limit A (λn (a,q). P n a q) (λ(a,q). qpochhammer_inf a q) at_top"
  proof (intro uniform_limit_cong)
    fix aq :: "'a × 'a"
    assume "aq  A"
    then obtain a q where [simp]: "aq = (a, q)" and aq: "(a, q)  A"
      by (cases aq) auto
    from aq and assms have q: "norm q < 1"
      by auto
    have "(λn. case aq of (a, q)  P n a q)  g aq"
      by (rule tendsto_uniform_limitI[OF g]) fact
    hence "(λn. case aq of (a, q)  P (Suc n) a q)  g aq"
      by (rule filterlim_compose) (rule filterlim_Suc)
    moreover have "(λn. case aq of (a, q)  P (Suc n) a q)  qpochhammer_inf a q"
      using convergent_prod_LIMSEQ[OF convergent_qpochhammer_inf[of q a]] aq q
      unfolding P_def lessThan_Suc_atMost
      by (simp add: qpochhammer_inf_def)
    ultimately show "g aq = (case aq of (a, q)  qpochhammer_inf a q)"
      using tendsto_unique by force
  qed auto
  finally show ?thesis .
qed

lemma continuous_on_qpochhammer_inf [continuous_intros]:
  fixes a q :: "'b :: topological_space  'a"
  assumes [continuous_intros]: "continuous_on A a" "continuous_on A q"
  assumes "x. x  A  norm (q x) < 1"
  shows   "continuous_on A (λx. qpochhammer_inf (a x) (q x))"
proof -
  have *: "continuous_on (cball 0 ra × cball 0 rq) (λ(a,q). qpochhammer_inf a q :: 'a)"
    if r: "0  ra" "0  rq" "rq < 1" for ra rq :: real
  proof (rule uniform_limit_theorem)
    show "uniform_limit (cball 0 ra × cball 0 rq) (λn (a,q). P n a q)
            (λ(a,q). qpochhammer_inf a q) at_top"
      by (rule uniform_limit_qpochhammer_inf) (use r in auto simp: compact_Times)
  qed (auto intro!: always_eventually intro!: continuous_intros simp: P_def case_prod_unfold)

  have **: "isCont (λ(a,q). qpochhammer_inf a q) (a, q)" if q: "norm q < 1" for a q :: 'a
  proof -
    obtain R where R: "norm q < R" "R < 1"
      using dense q by blast
    with norm_ge_zero[of q] have "R  0"
      by linarith
    have "continuous_on (cball 0 (norm a + 1) × cball 0 R) (λ(a,q). qpochhammer_inf a q :: 'a)"
      by (rule *) (use R R  0 in auto)
    hence "continuous_on (ball 0 (norm a + 1) × ball 0 R) (λ(a,q). qpochhammer_inf a q :: 'a)"
      by (rule continuous_on_subset) auto
    moreover have "(a, q)  ball 0 (norm a + 1) × ball 0 R"
      using q R by auto
    ultimately show ?thesis
      by (subst (asm) continuous_on_eq_continuous_at) (auto simp: open_Times)
  qed
  hence ***: "continuous_on ((λx. (a x, q x)) ` A) (λ(a,q). qpochhammer_inf a q)"
    using assms(3) by (intro continuous_at_imp_continuous_on) auto
  have "continuous_on A ((λ(a,q). qpochhammer_inf a q)  (λx. (a x, q x)))"
    by (rule continuous_on_compose[OF _ ***]) (intro continuous_intros)
  thus ?thesis
    by (simp add: o_def)
qed

lemma continuous_qpochhammer_inf [continuous_intros]:
  fixes a q :: "'b :: t2_space  'a"
  assumes "continuous (at x within A) a" "continuous (at x within A) q" "norm (q x) < 1"
  shows   "continuous (at x within A) (λx. qpochhammer_inf (a x) (q x))"
proof -
  have "continuous_on (UNIV × ball 0 1) (λx. qpochhammer_inf (fst x) (snd x) :: 'a)"
    by (intro continuous_intros) auto
  moreover have "(a x, q x)  UNIV × ball 0 1"
    using assms(3) by auto
  ultimately have "isCont (λx. qpochhammer_inf (fst x) (snd x)) (a x, q x)"
    by (simp add: continuous_on_eq_continuous_at open_Times)
  hence "continuous (at (a x, q x) within (λx. (a x, q x)) ` A) 
           (λx. qpochhammer_inf (fst x) (snd x))"
    using continuous_at_imp_continuous_at_within by blast
  hence "continuous (at x within A) ((λx. qpochhammer_inf (fst x) (snd x))  (λx. (a x, q x)))"
    by (intro continuous_intros assms)
  thus ?thesis
    by (simp add: o_def)
qed

lemma tendsto_qpochhammer_inf [tendsto_intros]:
  fixes a q :: "'b  'a"
  assumes "(a  a0) F" "(q  q0) F" "norm q0 < 1"
  shows   "((λx. qpochhammer_inf (a x) (q x))  qpochhammer_inf a0 q0) F"
proof -
  define f where "f = (λx. qpochhammer_inf (fst x) (snd x) :: 'a)"
  have "((λx. f (a x, q x))  f (a0, q0)) F"
  proof (rule isCont_tendsto_compose[of _ f])
    show "isCont f (a0, q0)"
      using assms(3) by (auto simp: f_def intro!: continuous_intros)
    show "((λx. (a x, q x))  (a0, q0)) F "
      by (intro tendsto_intros assms)
  qed
  thus ?thesis
    by (simp add: f_def)
qed

end

context
  fixes P :: "nat  complex  complex  complex"
  defines "P  (λN a q. n<N. 1 - a * q ^ n)"
begin

lemma holomorphic_qpochhammer_inf [holomorphic_intros]:
  assumes [holomorphic_intros]: "a holomorphic_on A" "q holomorphic_on A"
  assumes "x. x  A  norm (q x) < 1" "open A"
  shows   "(λx. qpochhammer_inf (a x) (q x)) holomorphic_on A"
proof (rule holomorphic_uniform_sequence)
  fix x assume x: "x  A"
  then obtain r where r: "r > 0" "cball x r  A"
    using open A unfolding open_contains_cball by blast
  have *: "compact ((λx. (a x, q x)) ` cball x r)" using r
    by (intro compact_continuous_image continuous_intros)
       (auto intro!: holomorphic_on_imp_continuous_on[OF holomorphic_on_subset] holomorphic_intros)
  have "uniform_limit ((λx. (a x, q x)) ` cball x r) (λn (a,q). P n a q) (λ(a,q). qpochhammer_inf a q) at_top"
    unfolding P_def
    by (rule uniform_limit_qpochhammer_inf[OF *]) (use r assms(3) in auto simp: compact_Times)
  hence "uniform_limit (cball x r) (λn x. case (a x, q x) of (a, q)  P n a q)
           (λx. case (a x, q x) of (a, q)  qpochhammer_inf a q) at_top"
    by (rule uniform_limit_compose') auto
  thus "d>0. cball x d  A  uniform_limit (cball x d)
            (λn x. case (a x, q x) of (a, q)  P n a q)
            (λx. qpochhammer_inf (a x) (q x)) sequentially"
    using r by fast
qed (use open A in auto intro!: holomorphic_intros simp: P_def)

lemma analytic_qpochhammer_inf [analytic_intros]:
  assumes [analytic_intros]: "a analytic_on A" "q analytic_on A"
  assumes "x. x  A  norm (q x) < 1"
  shows   "(λx. qpochhammer_inf (a x) (q x)) analytic_on A"
proof -
  from assms(1) obtain A1 where A1: "open A1" "A  A1" "a holomorphic_on A1"
    by (auto simp: analytic_on_holomorphic)
  from assms(2) obtain A2 where A2: "open A2" "A  A2" "q holomorphic_on A2"
    by (auto simp: analytic_on_holomorphic)
  have "continuous_on A2 q"
    by (rule holomorphic_on_imp_continuous_on) fact
  hence "open (q -` ball 0 1  A2)"
    using A2 by (subst (asm) continuous_on_open_vimage) auto
  define A' where "A' = (q -` ball 0 1  A2)  A1"
  have "open A'"
    unfolding A'_def by (rule open_Int) fact+

  note [holomorphic_intros] = holomorphic_on_subset[OF A1(3)] holomorphic_on_subset[OF A2(3)]
  have "(λx. qpochhammer_inf (a x) (q x)) holomorphic_on A'"
    using open A' by (intro holomorphic_intros) (auto simp: A'_def)
  moreover have "A  A'"
    using A1(2) A2(2) assms(3) unfolding A'_def by auto
  ultimately show ?thesis
    using analytic_on_holomorphic open A' by blast
qed  

lemma meromorphic_qpochhammer_inf [meromorphic_intros]:
  assumes [analytic_intros]: "a analytic_on A" "q analytic_on A"
  assumes "x. x  A  norm (q x) < 1"
  shows   "(λx. qpochhammer_inf (a x) (q x)) meromorphic_on A"
  by (rule analytic_on_imp_meromorphic_on) (use assms(3) in auto intro!: analytic_intros)

end


subsection ‹Bounds for $(a; q)_n$ and $\binom{n}{k}_q$ in terms of $(a; q)_\infty$›

lemma qpochhammer_le_qpochhammer_inf:
  assumes "q  0" "q < 1" "a  0"
  shows   "qpochhammer (int k) a q  qpochhammer_inf a (q::real)"
  unfolding qpochhammer_nonneg_def qpochhammer_inf_def
proof (rule prod_le_prodinf)
  show "(λk. 1 - a * q ^ k) has_prod qpochhammer_inf a q"
    by (rule has_prod_qpochhammer_inf) (use assms in auto)
next
  fix i :: nat
  have *: "a * q ^ i  0"
    by (rule mult_nonpos_nonneg) (use assms in auto)
  show "1 - a * q ^ i  0"  "1  1 - a * q ^ i"
    using * by simp_all
qed

lemma qpochhammer_ge_qpochhammer_inf:
  assumes "q  0" "q < 1" "a  0" "a  1"
  shows   "qpochhammer (int k) a q  qpochhammer_inf a (q::real)"
  unfolding qpochhammer_nonneg_def qpochhammer_inf_def
proof (rule prod_ge_prodinf)
  show "(λk. 1 - a * q ^ k) has_prod qpochhammer_inf a q"
    by (rule has_prod_qpochhammer_inf) (use assms in auto)
next
  fix i :: nat
  have "a * q ^ i  1 * 1 ^ i"
    using assms by (intro mult_mono power_mono) auto
  thus "1 - a * q ^ i  0"
    by auto
  show "1 - a * q ^ i  1"
    using assms by auto
qed  

lemma norm_qbinomial_le_qpochhammer_inf_strong:
  fixes q :: "'a :: {real_normed_field}"
  assumes q: "norm q < 1"
  shows   "norm (qbinomial q n k) 
             qpochhammer_inf (-(norm q ^ (n + 1 - k))) (norm q) /
             qpochhammer_inf (norm q) (norm q)"
proof (cases "k  n")
  case k: True
  have "norm (qbinomial q n k ) =
          norm (qpochhammer (int k) (q ^ (n + 1 - k)) q) /
          norm (qpochhammer (int k) q q)"
    using q k by (subst qbinomial_conv_qpochhammer') (simp_all add: norm_divide)
  also have "  qpochhammer (int k) (-norm (q ^ (n + 1 - k))) (norm q) /
                  qpochhammer (int k) (norm q) (norm q)"
    by (intro frac_le norm_qpochhammer_nonneg_le_qpochhammer norm_qpochhammer_nonneg_ge_qpochhammer
                 qpochhammer_nonneg qpochhammer_pos)
       (use assms in auto intro: order.trans[OF _ norm_ge_zero])
  also have "  qpochhammer_inf (-(norm (q ^ (n+1-k)))) (norm q) / qpochhammer_inf (norm q) (norm q)"
    by (intro frac_le qpochhammer_le_qpochhammer_inf qpochhammer_ge_qpochhammer_inf
              qpochhammer_inf_pos qpochhammer_inf_nonneg)
       (use assms in auto simp: norm_power power_le_one_iff simp del: power_Suc)
  finally show ?thesis
    by (simp_all add: norm_power)
qed (use q in auto intro!: divide_nonneg_nonneg qpochhammer_inf_nonneg)

lemma norm_qbinomial_le_qpochhammer_inf:
  fixes q :: "'a :: {real_normed_field}"
  assumes q: "norm q < 1"
  shows   "norm (qbinomial q n k) 
             qpochhammer_inf (-norm q) (norm q) / qpochhammer_inf (norm q) (norm q)"
proof (cases "k  n")
  case True
  have "norm (qbinomial q n k) 
          qpochhammer_inf (-(norm q ^ (n + 1 - k))) (norm q) /
          qpochhammer_inf (norm q) (norm q)"
    by (rule norm_qbinomial_le_qpochhammer_inf_strong) (use q in auto)
  also have "  qpochhammer_inf (-norm q) (norm q) / qpochhammer_inf (norm q) (norm q)"
  proof (rule divide_right_mono)
    show "qpochhammer_inf (- (norm q ^ (n + 1 - k))) (norm q)  qpochhammer_inf (- norm q) (norm q)"
    proof (intro has_prod_le[OF has_prod_qpochhammer_inf has_prod_qpochhammer_inf] conjI)
      fix i :: nat
      have "norm q ^ (n + 1 - k + i)  norm q ^ (Suc i)"
        by (intro power_decreasing) (use assms True in simp_all)
      thus "1 - - (norm q ^ (n + 1 - k)) * norm q ^ i  1 - - norm q * norm q ^ i"
        by (simp_all add: power_add)
    qed (use assms in auto)
  qed (use assms in auto intro!: qpochhammer_inf_nonneg)
  finally show ?thesis .
qed (use q in auto intro!: divide_nonneg_nonneg qpochhammer_inf_nonneg)



subsection ‹Limits of the $q$-binomial coefficients›

text ‹
  The following limit is Fact~7.7 in Andrews \& Eriksson~citeandrews2004.
›
lemma tendsto_qbinomial1:
  fixes q :: "'a :: {real_normed_field, banach, heine_borel}"
  assumes q: "norm q < 1"
  shows   "(λn. qbinomial q n m)  1 / qpochhammer m q q"
proof -
  have not_one: "q ^ k  1" if "k > 0" for k :: nat
    using q_power_neq_1[of q k] that q by simp
  have [simp]: "q  1"
    using q by auto

  define P where "P = (λn. qpochhammer (int n) q q)"
  have [simp]: "qpochhammer_inf q q  0"
    using q by (auto simp: qpochhammer_inf_zero_iff not_one simp flip: power_Suc)
  have [simp]: "P m  0"
  proof
    assume "P m = 0"
    then obtain k where k: "q * q powi k = 1" "k  {0..<int m}"
      by (auto simp: P_def qpochhammer_eq_0_iff power_int_add)
    show False
      by (use k not_one[of "Suc (nat k)"] in auto simp: power_int_add power_int_def)
  qed

  have [tendsto_intros]: "(λn. P (h n))  qpochhammer_inf q q" 
    if h: "filterlim h at_top at_top" for h :: "nat  nat"
    unfolding P_def using filterlim_compose[OF qpochhammer_tendsto_qpochhammer_inf[OF q] h, of q] .
  have "(λn. P n / (P m * P (n - m)))  1 / P m"
    by (auto intro!: tendsto_eq_intros filterlim_ident filterlim_minus_const_nat_at_top)
  also have "(F n in at_top. P n / (P m * P (n - m)) = qbinomial q n m)"
    using eventually_ge_at_top[of m]
    by eventually_elim (auto simp: qbinomial_conv_qpochhammer P_def not_one of_nat_diff)
  hence "(λn. P n / (P m * P (n - m)))  1 / P m 
         (λn. qbinomial q n m)  1 / P m" 
    by (intro filterlim_cong) auto
  finally show "(λn. qbinomial q n m)  1 / qpochhammer m q q"
    unfolding P_def .
qed
  
text ‹
  The following limit is a slightly stronger version of Fact~7.8 in 
  Andrews \& Eriksson~citeandrews2004. Their version has $f(n) = rn + c_1$ and 
  $g(n) = sn + c_2$ with $r > s$.
›
lemma tendsto_qbinomial2:
  fixes q :: "'a :: {real_normed_field, banach, heine_borel}"
  assumes q: "norm q < 1"
  assumes lim_fg: "filterlim (λn. f n - g n) at_top F"
  assumes lim_g: "filterlim g at_top F"
  shows   "((λn. qbinomial q (f n) (g n))  1 / qpochhammer_inf q q) F"
proof -
  have not_one: "q ^ k  1" if "k > 0" for k :: nat
    using q_power_neq_1[of q k] that q by simp
  have [simp]: "q  1"
    using q by auto

  define P where "P = (λn. qpochhammer (int n) q q)"
  have [simp]: "qpochhammer_inf q q  0"
    using q by (auto simp: qpochhammer_inf_zero_iff not_one simp flip: power_Suc)
  have lim_f: "filterlim f at_top F"
    using lim_fg by (rule filterlim_at_top_mono) auto

  have fg: "eventually (λn. f n  g n) F"
  proof -
    have "eventually (λn. f n - g n > 0) F"
      using lim_fg by (metis eventually_gt_at_top filterlim_iff)
    thus ?thesis
      by eventually_elim auto
  qed
  from lim_g and fg have lim_f: "filterlim f at_top F"
    using filterlim_at_top_mono by blast

  have [tendsto_intros]: "((λn. P (h n))  qpochhammer_inf q q) F"
    if h: "filterlim h at_top F" for h
    unfolding P_def using filterlim_compose[OF qpochhammer_tendsto_qpochhammer_inf[OF q] h, of q] .
  have "((λn. P (f n) / (P (g n) * P (f n - g n)))  1 / qpochhammer_inf q q) F"
    by (auto intro!: tendsto_eq_intros lim_f lim_g lim_fg)
  also from fg have "(F n in F. P (f n) / (P (g n) * P (f n - g n)) = qbinomial q (f n) (g n))"
    by eventually_elim
       (auto simp: qbinomial_qfact not_one of_nat_diff qfact_conv_qpochhammer
                   power_int_minus power_int_diff P_def field_simps)
  hence "((λn. P (f n) / (P (g n) * P (f n - g n)))  1 / qpochhammer_inf q q) F 
         ((λn. qbinomial q (f n) (g n))  1 / qpochhammer_inf q q) F"
    by (intro filterlim_cong) auto
  finally show "((λn. qbinomial q (f n) (g n))  1 / qpochhammer_inf q q) F" .
qed


subsection ‹Useful identities›

text ‹
  The following lemmas give a recurrence for the infinite $q$-Pochhammer symbol similar to
  the one for the ``normal'' Pochhammer symbol.
›
lemma qpochhammer_inf_mult_power_q:
  assumes "norm q < 1"
  shows   "qpochhammer_inf a q = qpochhammer (int n) a q * qpochhammer_inf (a * q ^ n) q"
proof -
  have "(λn. 1 - a * q ^ n) has_prod qpochhammer_inf a q"
    by (rule has_prod_qpochhammer_inf) (use assms in auto)
  hence "convergent_prod (λn. 1 - a * q ^ n)"
    by (simp add: has_prod_iff)
  hence "(λn. 1 - a * q ^ n) has_prod
          ((k<n. 1 - a * q ^ k) * (k. 1 - a * q ^ (k + n)))"
    by (intro has_prod_ignore_initial_segment')
  also have "(k. 1 - a * q ^ (k + n)) = (k. 1 - (a * q ^ n) * q ^ k)"
    by (simp add: power_add mult_ac)
  also have "(λk. 1 - (a * q ^ n) * q ^ k) has_prod qpochhammer_inf (a * q ^ n) q"
    by (rule has_prod_qpochhammer_inf) (use assms in auto)
  hence "(k. 1 - (a * q ^ n) * q ^ k) = qpochhammer_inf (a * q ^ n) q"
    by (simp add: qpochhammer_inf_def)
  finally show ?thesis
    by (simp add: qpochhammer_inf_def has_prod_iff qpochhammer_nonneg_def)
qed

text ‹
  One can express the finite $q$-Pochhammer symbol in terms of the infinite one:
  \[(a; q)_n = \frac{(a; q)_\infty}{(a; q^n)_\infty}\]
›
lemma qpochhammer_conv_qpochhammer_inf_nonneg:
  assumes "norm q < 1" "m. m  n  a * q ^ m  1"
  shows   "qpochhammer (int n) a q = qpochhammer_inf a q / qpochhammer_inf (a * q ^ n) q"
proof (cases "qpochhammer_inf (a * q ^ n) q = 0")
  case False
  thus ?thesis
    by (subst qpochhammer_inf_mult_power_q[OF assms(1), of _ n]) 
      (auto simp: qpochhammer_inf_zero_iff)
next
  case True
  with assms obtain k where "a * q ^ (n + k) = 1"
    by (auto simp: qpochhammer_inf_zero_iff power_add mult_ac)
  moreover have "n + k  n"
    by auto
  ultimately have "mn+k. a * q ^ m = 1"
    by blast
  with assms have False
    by auto
  thus ?thesis ..
qed

lemma qpochhammer_conv_qpochhammer_inf:
  fixes q a :: "'a :: {real_normed_field, banach, heine_borel}"
  assumes q: "norm q < 1" "n < 0  q  0"
  assumes not_one: "k. int k  n  a * q ^ k  1"
  shows "qpochhammer n a q = qpochhammer_inf a q / qpochhammer_inf (a * q powi n) q"
proof (cases "n  0")
  case n: True
  define m where "m = nat n"
  have n_eq: "n = int m"
    using n by (auto simp: m_def)
  show ?thesis unfolding n_eq
    by (subst qpochhammer_conv_qpochhammer_inf_nonneg) (use q not_one in auto simp: n_eq)
next
  case n: False
  define m where "m = nat (-n)"
  have n_eq: "n = -int m" and m: "m > 0"
    using n by (auto simp: m_def)
  have nz: "qpochhammer_inf a q  0"
    using q not_one n by (auto simp: qpochhammer_inf_zero_iff)
  have "qpochhammer n a q = 1 / qpochhammer (int m) (a / q ^ m) q"
    using m > 0 by (simp add: n_eq qpochhammer_minus)
  also have " = qpochhammer_inf a q / qpochhammer_inf (a / q ^ m) q"
    using qpochhammer_inf_mult_power_q[OF q(1), of "a / q ^ m" m] nz q n
    by (auto simp: divide_simps)
  also have "a / q ^ m = a * q powi n"
    by (simp add: n_eq power_int_minus field_simps)
  finally show "qpochhammer n a q = qpochhammer_inf a q / qpochhammer_inf (a * q powi n) q" .
qed

lemma qpochhammer_inf_divide_power_q:
  assumes "norm q < 1" and [simp]: "q  0"
  shows   "qpochhammer_inf (a / q ^ n) q = (k = 1..n. 1 - a / q ^ k) * qpochhammer_inf a q"
proof -
  have "qpochhammer_inf (a / q ^ n) q =
          qpochhammer (int n) (a / q ^ n) q * qpochhammer_inf (a / q^n * q^n) q"
    using assms(1) by (rule qpochhammer_inf_mult_power_q)
  also have "qpochhammer (int n) (a / q ^ n) q = (k<n. 1 - a / q ^ (n - k))"
    unfolding qpochhammer_nonneg_def by (intro prod.cong) (auto simp: power_diff)
  also have " = (k=1..n. 1 - a / q ^ k)"
    by (intro prod.reindex_bij_witness[of _ "λk. n - k" "λk. n - k"]) auto
  finally show ?thesis
    by simp
qed

lemma qpochhammer_inf_mult_q:
  assumes "norm q < 1"
  shows   "qpochhammer_inf a q = (1 - a) * qpochhammer_inf (a * q) q"
  using qpochhammer_inf_mult_power_q[OF assms, of a 1] by simp

lemma qpochhammer_inf_divide_q:
  assumes "norm q < 1" "q  0"
  shows   "qpochhammer_inf (a / q) q = (1 - a / q) *  qpochhammer_inf a q"
  using qpochhammer_inf_divide_power_q[of q a 1] assms by simp


text ‹
  The following lemma allows combining a product of several $q$-Pochhammer symbols into one
  by grouping factors:
  \[(a; q^m)_\infty\, (aq; q^m)_\infty\, \cdots\, (aq^{m-1}; q^m)_\infty = (a; q)_\infty\]
›
lemma prod_qpochhammer_group:
  assumes "norm q < 1" and "m > 0"
  shows   "(i<m. qpochhammer_inf (a * q^i) (q^m)) = qpochhammer_inf a q"
proof (rule has_prod_unique2)
  show "(λn. (i<m. 1 - a * q^i * (q^m) ^ n)) has_prod (i<m. qpochhammer_inf (a * q^i) (q^m))"
    by (intro has_prod_prod has_prod_qpochhammer_inf)
       (use assms in auto simp: norm_power power_less_one_iff)
next
  have "(λn. 1 - a * q ^ n) has_prod qpochhammer_inf a q"
    by (rule has_prod_qpochhammer_inf) (use assms in auto)
  hence "(λn. i=n*m..<n*m+m. 1 - a * q^i) has_prod qpochhammer_inf a q"
    by (rule has_prod_group) (use assms in auto)
  also have "(λn. i=n*m..<n*m+m. 1 - a * q^i) = (λn. i<m. 1 - a * q ^ i * (q ^ m) ^ n)"
  proof
    fix n :: nat
    have "(i=n*m..<n*m+m. 1 - a * q^i) = (i<m. 1 - a * q^(n*m + i))"
      by (intro prod.reindex_bij_witness[of _ "λi. i + n * m" "λi. i - n * m"]) auto
    thus "(i=n*m..<n*m+m. 1 - a * q^i) = (i<m. 1 - a * q ^ i * (q ^ m) ^ n)"
      by (simp add: power_add mult_ac flip: power_mult)
  qed
  finally show "(λn. (i<m. 1 - a * q^i * (q^m) ^ n)) has_prod qpochhammer_inf a q" .
qed

text ‹
  A product of two $q$-Pochhammer symbols $(\pm a; q)_\infty$ can be combined into
  a single $q$-Pochhammer symbol:
›
lemma qpochhammer_inf_square:
  assumes q: "norm q < 1"
  shows   "qpochhammer_inf a q * qpochhammer_inf (-a) q = qpochhammer_inf (a^2) (q^2)"
          (is "?lhs = ?rhs")
proof -
  have "(λn. (1 - a * q ^ n) * (1 - (-a) * q ^ n)) has_prod
          (qpochhammer_inf a q * qpochhammer_inf (-a) q)"
    by (intro has_prod_qpochhammer_inf has_prod_mult) (use q in auto)
  also have "(λn. (1 - a * q ^ n) * (1 - (-a) * q ^ n)) = (λn. (1 - a ^ 2 * (q ^ 2) ^ n))"
    by (auto simp: fun_eq_iff algebra_simps power2_eq_square simp flip: power_add mult_2)
  finally have "(λn. (1 - a ^ 2 * (q ^ 2) ^ n)) has_prod ?lhs" .
  moreover have "(λn. (1 - a ^ 2 * (q ^ 2) ^ n)) has_prod qpochhammer_inf (a^2) (q^2)"
    by (intro has_prod_qpochhammer_inf) (use assms in auto simp: norm_power power_less_one_iff)
  ultimately show ?thesis
    using has_prod_unique2 by blast
qed


subsection ‹Two series expansions by Euler›

text ‹
  The following two theorems and their proofs are taken from Bellman~\cite{bellman1961}[\S 40].
  He credits them, in their original form, to Euler. One could also deduce these relatively
  easily from the infinite version of the $q$-binomial theorem (which we will prove later),
  but the proves given by Bellman are so nice that I do not want to omit them from here.

  The first theorem states that for any complex $x,t$ with $|x|<1$, we have:
  \[(t; x)_\infty = \prod_{k=0}^\infty (1 - tx^k) = 
      \sum_{n=0}^\infty \frac{x^{n(n-1)/2} t^n}{(x-1) \cdots (x^n-1)}\]
  This tells us the power series expansion for $f_x(t) = (t; x)_\infty$.
›
lemma
  fixes x :: complex
  assumes x: "norm x < 1"
  shows sums_qpochhammer_inf_complex:
          "(λn. x^(n*(n-1) div 2) * t^n / (k=1..n. x^k - 1)) sums qpochhammer_inf t x"
    and has_fps_expansion_qpochhammer_inf_complex:
          "(λt. qpochhammer_inf t x) has_fps_expansion
             Abs_fps (λn. x^(n*(n-1) div 2) / (k=1..n. x^k - 1))"
proof -
  text ‹
    For a fixed $x$, we define $f(t) = (t; x)_\infty$ and note that $f$ satisfies the
    functional equation $f(t) = (1-t) f(tx)$.
  ›
  define f where "f = (λt. qpochhammer_inf t x)"
  have f_eq: "f t = (1 - t) * f (t * x)" for t
    unfolding f_def using qpochhammer_inf_mult_q[of x t] x by simp
  define F where "F = fps_expansion f 0"
  define a where "a = fps_nth F"
  have ana: "f analytic_on UNIV"
    unfolding f_def by (intro analytic_intros) (use x in auto)

  text ‹
    We note that $f$ is entire and therefore has a Maclaurin expansion, say
    $f(t) = \sum_{n=0}^\infty a_n x^n$.
  ›
  have F: "f has_fps_expansion F"
    unfolding F_def by (intro analytic_at_imp_has_fps_expansion_0 analytic_on_subset[OF ana]) auto
  have "fps_conv_radius F  "
    unfolding F_def by (rule conv_radius_fps_expansion) (auto intro!: analytic_imp_holomorphic ana)
  hence [simp]: "fps_conv_radius F = "
    by simp
  have F_sums: "(λn. fps_nth F n * t ^ n) sums f t" for t
  proof -
    have "(λn. fps_nth F n * t ^ n) sums eval_fps F t"
      using sums_eval_fps[of t F] by simp
    also have "eval_fps F t = f t"
      by (rule has_fps_expansion_imp_eval_fps_eq[OF F, of _ "norm t + 1"])
         (auto intro!: analytic_imp_holomorphic analytic_on_subset[OF ana])
    finally show ?thesis .
  qed

  have F_eq: "F = (1 - fps_X) * (F oo (fps_const x * fps_X))"
  proof -
    have "(λt. (1 - t) * (f  (λt. t * x)) t) has_fps_expansion 
            (fps_const 1 - fps_X) * (F oo (fps_X * fps_const x))"
      by (intro fps_expansion_intros F) auto
    also have " = (1 - fps_X) * (F oo (fps_const x * fps_X))"
      by (simp add: mult_ac)
    also have "(λt. (1 - t) * (f  (λt. t * x)) t) = f"
      unfolding o_def by (intro ext f_eq [symmetric])
    finally show "F = (1 - fps_X) * (F oo (fps_const x * fps_X))"
      using F fps_expansion_unique_complex by blast
  qed

  have a_0 [simp]: "a 0 = 1"
    using has_fps_expansion_imp_0_eq_fps_nth_0[OF F] by (simp add: a_def f_def)

  text ‹
    Applying the functional equation to the Maclaurin series, we obtain a recurrence
    for the coefficients $a_n$, namely $a_{n+1} = \frac{a_n x^n}{x^{n+1} - 1}$.
  ›
  have a_rec: "(x ^ Suc n - 1) * a (Suc n) = x ^ n * a n" for n
  proof -
    have "a (Suc n) = fps_nth F (Suc n)"
      by (simp add: a_def)
    also have "F = (F oo (fps_const x * fps_X)) - fps_X * (F oo (fps_const x * fps_X))"
      by (subst F_eq) (simp_all add: algebra_simps)
    also have "fps_nth  (Suc n) = x ^ Suc n * a (Suc n) - x ^ n * a n"
      by (simp add: fps_compose_linear a_def)
    finally show "(x ^ Suc n - 1) * a (Suc n) = x ^ n * a n"
      by (simp add: algebra_simps)
  qed

  define tri where "tri = (λn::nat. n * (n-1) div 2)"
  have not_one: "x ^ k  1" if k: "k > 0" for k :: nat
  proof -
    have "norm (x ^ k) < 1"
      using x k by (simp add: norm_power power_less_one_iff)
    thus ?thesis
      by auto
  qed

  text ‹
    The recurrence is easily solved and we get 
    $a_n = x^{n(n-1)/2}{(x-1)(x^2-1)\cdots(x^n - 1)}$.
  ›
  have a_sol: "(k=1..n. (x^k - 1)) * a n = x ^ tri n" for n
  proof (induction n)
    case 0
    thus ?case
      by (simp add: tri_def)
  next
    case (Suc n)
    have "(k=1..Suc n. (x^k - 1)) * a (Suc n) =
          (k=1..n. x ^ k - 1) * ((x ^ Suc n - 1) * a (Suc n))"
      by (simp add: a_rec mult_ac)
    also have " = (k = 1..n. x ^ k - 1) * a n * x ^ n"
      by (subst a_rec) simp_all
    also have "(k=1..n. x ^ k - 1) * a n = x ^ tri n"
      by (subst Suc.IH) auto
    also have "x ^ tri n * x ^ n = x ^ (tri n + (2*n) div 2)"
      by (simp add: power_add)
    also have "tri n + (2*n) div 2 = tri (Suc n)"
      unfolding tri_def
      by (subst div_plus_div_distrib_dvd_left [symmetric]) (auto simp: algebra_simps)
    finally show ?case .
  qed

  have a_sol': "a n = x ^ tri n / (k=1..n. (x ^ k - 1))" for n
    using not_one a_sol[of n] by (simp add: divide_simps mult_ac)

  show "(λn. x ^ tri n * t ^ n / (k=1..n. x ^ k - 1)) sums f t"
    using F_sums[of t] a_sol' by (simp add: a_def)

  have "F = Abs_fps (λn. x^(n*(n-1) div 2) / (k=1..n. x^k - 1))"
    by (rule fps_ext) (simp add: a_sol'[unfolded a_def] tri_def)
  thus "f has_fps_expansion Abs_fps (λn. x^(n*(n-1) div 2) / (k=1..n. x^k - 1))"
    using F by simp
qed

lemma sums_qpochhammer_inf_real:
  assumes "¦x¦ < (1 :: real)"
  shows   "(λn. x^(n*(n-1) div 2) * t^n / (k=1..n. x^k - 1)) sums qpochhammer_inf t x"
proof -
  have "(λn. complex_of_real x ^ (n*(n-1) div 2) * of_real t ^ n / (k=1..n. of_real x ^ k - 1)) 
          sums qpochhammer_inf (of_real t) (of_real x)" (is "?f sums ?S")
    by (intro sums_qpochhammer_inf_complex) (use assms in auto)
  also have "?f = (λn. complex_of_real (x ^ (n*(n-1) div 2) * t ^ n / (k=1..n. x ^ k - 1)))"
    by simp
  also have "qpochhammer_inf (of_real t) (of_real x) = complex_of_real (qpochhammer_inf t x)"
    by (rule qpochhammer_inf_of_real) fact
  finally show ?thesis
    by (subst (asm) sums_of_real_iff)
qed

lemma norm_summable_qpochhammer_inf:
  fixes x t :: "'a :: {real_normed_field}"
  assumes "norm x < 1"
  shows   "summable (λn. norm (x^(n*(n-1) div 2) * t ^ n / (k=1..n. x^k - 1)))"
proof -
  have "norm x < 1"
    using assms by simp
  then obtain r where "norm x < r" "r < 1"
    using dense by blast
  hence r: "0 < r" "norm x < r" "r < 1"
    using le_less_trans[of 0 "norm x" r] by auto
  define R where "R = Max {2, norm t, r + 1}"
  have R: "r < R" "norm t  R" "R > 1"
    unfolding R_def by auto

  show ?thesis
  proof (rule summable_comparison_test_bigo)
    show "summable (λn. norm ((1/2::real) ^ n))"
      unfolding norm_power norm_divide by (rule summable_geometric) (use r in auto)
  next
    have "(λn. norm (x ^ (n * (n - 1) div 2) * t ^ n / (k = 1..n. x ^ k - 1)))  
            O(λn. r^(n*(n-1) div 2) * R ^ n / (1 - r) ^ n)"
    proof (rule bigoI[of _ 1], intro always_eventually allI)
      fix n :: nat
      have "norm (norm (x^(n*(n-1) div 2) * t ^ n / (k=1..n. x^k - 1))) =
              norm x ^ (n * (n - 1) div 2) * norm t ^ n / (k=1..n. norm (1 - x ^ k))"
        by (simp add: norm_power norm_mult norm_divide norm_minus_commute abs_prod flip: prod_norm)
      also have "  norm x ^ (n * (n - 1) div 2) * norm t ^ n / (k=1..n. 1 - norm x)"
      proof (intro divide_left_mono mult_pos_pos prod_pos prod_mono conjI mult_nonneg_nonneg)
        fix k :: nat assume k: "k  {1..n}"
        have "norm x ^ k  norm x ^ 1"
          by (intro power_decreasing) (use assms k in auto)
        hence "1 - norm x  norm (1::'a) - norm (x ^ k)"
          by (simp add: norm_power)
        also have "  norm (1 - x ^ k)"
          by norm
        finally show "1 - norm x  norm (1 - x ^ k)" .
        have "0 < 1 - norm x"
          using assms by simp
        also have "  norm (1 - x ^ k)"
          by fact
        finally show "norm (1 - x ^ k) > 0" .
      qed (use assms in auto)
      also have "(k=1..n. 1 - norm x) = (1 - norm x) ^ n"
        by simp
      also have "norm x ^ (n*(n-1) div 2) * norm t ^ n / (1 - norm x) ^ n  
                 r ^ (n*(n-1) div 2) * R ^ n / (1 - r) ^ n"
        by (intro frac_le mult_mono power_mono) (use r R in auto)
      also have "  abs (r^(n*(n-1) div 2) * R ^ n / (1 - r) ^ n)"
        by linarith
      finally show "norm (norm (x ^ (n * (n - 1) div 2) * t ^ n / (k = 1..n. x ^ k - 1)))
                      1 * norm (r ^ (n * (n - 1) div 2) * R ^ n / (1 - r) ^ n)"
        by simp
    qed
    also have "(λn. r ^ (n*(n-1) div 2) * R ^ n / (1 - r) ^ n)  O(λn. (1/2) ^ n)"
      using r R by real_asymp
    finally show "(λn. norm (x ^ (n * (n - 1) div 2) * t ^ n / (k = 1..n. x ^ k - 1)))  
                    O(λn. (1/2) ^ n)" .
  qed
qed



text ‹
  The second theorem states that for any complex $x,t$ with $|x|<1$, $|t|<1$, we have:
  \[\frac{1}{(t; x)_\infty} = \prod_{k=0}^\infty \frac{1}{1 - tx^k} = 
      \sum_{n=0}^\infty \frac{t^n}{(1-x)\cdots(1-x^n)}\]
  This gives us the multiplicative inverse of the power series from the previous theorem.
›
lemma
  fixes x :: complex
  assumes x: "norm x < 1" and t: "norm t < 1"
  shows sums_inverse_qpochhammer_inf_complex:
          "(λn. t^n / (k=1..n. 1 - x^k)) sums inverse (qpochhammer_inf t x)"
    and has_fps_expansion_inverse_qpochhammer_inf_complex:
          "(λt. inverse (qpochhammer_inf t x)) has_fps_expansion
             Abs_fps (λn. 1 / (k=1..n. 1 - x^k))"
proof -
  text ‹
    The proof is very similar to the one before, except that our function is now
    $g(x) = 1 / (t; x)_\infty$ with the functional equation is $g(tx) = (1-t)g(t)$.
  ›
  define f where "f = (λt. qpochhammer_inf t x)"
  define g where "g = (λt. inverse (f t))"
  have f_nz: "f t  0" if t: "norm t < 1" for t
  proof
    assume "f t = 0"
    then obtain n where "t * x ^ n = 1"
      using x by (auto simp: qpochhammer_inf_zero_iff f_def mult_ac)
    have "norm (t * x ^ n) = norm t * norm (x ^ n)"
      by (simp add: norm_mult)
    also have "  norm t * 1"
      using x by (intro mult_left_mono) (auto simp: norm_power power_le_one_iff)
    also have "norm t < 1"
      using t by simp
    finally show False
      using t * x ^ n = 1 by simp
  qed

  have mult_less_1: "a * b < 1" if "0  a" "a < 1" "b  1" for a b :: real
  proof -
    have "a * b  a * 1"
      by (rule mult_left_mono) (use that in auto)
    also have "a < 1"
      by fact
    finally show ?thesis
      by simp
  qed

  have g_eq: "g (t * x) = (1 - t) * g(t)" if t: "norm t < 1" for t
  proof -
    have "f t = (1 - t) * f (t * x)"
      using qpochhammer_inf_mult_q[of x t] x
      by (simp add: algebra_simps power2_eq_square f_def)
    moreover have "norm (t * x) < 1"
      using t x by (simp add: norm_mult mult_less_1)
    ultimately show ?thesis
      using t by (simp add: g_def field_simps f_nz)
  qed


  define G where "G = fps_expansion g 0"
  define a where "a = fps_nth G"
  have [analytic_intros]: "f analytic_on A" for A
    unfolding f_def by (intro analytic_intros) (use x in auto)
  have ana: "g analytic_on ball 0 1" unfolding g_def 
    by (intro analytic_intros)
       (use x in auto simp: qpochhammer_inf_zero_iff f_nz)
  have G: "g has_fps_expansion G" unfolding G_def
    by (intro analytic_at_imp_has_fps_expansion_0 analytic_on_subset[OF ana]) auto
  have "fps_conv_radius G  1"
    unfolding G_def 
    by (rule conv_radius_fps_expansion) 
       (auto intro!: analytic_imp_holomorphic ana analytic_on_subset[OF ana])
  
  have G_sums: "(λn. fps_nth G n * t ^ n) sums g t" if t: "norm t < 1" for t
  proof -
    have "ereal (norm t) < 1"
      using t by simp
    also have "  fps_conv_radius G"
      by fact
    finally have "(λn. fps_nth G n * t ^ n) sums eval_fps G t"
      using sums_eval_fps[of t G] by simp
    also have "eval_fps G t = g t"
      by (rule has_fps_expansion_imp_eval_fps_eq[OF G, of _ 1])
         (auto intro!: analytic_imp_holomorphic analytic_on_subset[OF ana] t)
    finally show ?thesis .
  qed

  have G_eq: "(G oo (fps_const x * fps_X)) - (1 - fps_X) * G = 0"
  proof -
    define G' where "G' = (G oo (fps_const x * fps_X)) - (1 - fps_X) * G"
    have "(λt. (g  (λt. t * x)) t - (1 - t) * g t) has_fps_expansion G'"
      unfolding G'_def by (subst mult.commute, intro fps_expansion_intros G) auto
    also have "eventually (λt. t  ball 0 1) (nhds (0::complex))"
      by (intro eventually_nhds_in_open) auto
    hence "eventually (λt. (g  (λt. t * x)) t - (1 - t) * g t = 0) (nhds 0)"
      unfolding o_def by eventually_elim (subst g_eq, auto)
    hence "(λt. (g  (λt. t * x)) t - (1 - t) * g t) has_fps_expansion G' 
           (λt. 0) has_fps_expansion G'"
      by (intro has_fps_expansion_cong refl)
    finally have "G' = 0"
      by (rule fps_expansion_unique_complex) auto
    thus ?thesis
      unfolding G'_def .
  qed

  have not_one: "x ^ k  1" if k: "k > 0" for k :: nat
  proof -
    have "norm (x ^ k) < 1"
      using x k by (simp add: norm_power power_less_one_iff)
    thus ?thesis
      by auto
  qed

  have a_rec: " a (Suc m) = a m / (1 - x ^ Suc m)" for m
  proof -
    have "0 = fps_nth ((G oo (fps_const x * fps_X)) - (1 - fps_X) * G) (Suc m)"
      by (subst G_eq) simp_all
    also have " = (x ^ Suc m - 1) * a (Suc m) + a m"
      by (simp add: ring_distribs fps_compose_linear a_def)
    finally show ?thesis
      using not_one[of "Suc m"] by (simp add: field_simps)
  qed

  have a_0: "a 0 = 1"
    using has_fps_expansion_imp_0_eq_fps_nth_0[OF G] by (simp add: a_def f_def g_def)
  have a_sol: "a n = 1 / (k=1..n. (1 - x^k))" for n
    by (induction n) (simp_all add: a_0 a_rec)

  show "(λn. t^n / (k=1..n. 1 - x ^ k)) sums (inverse (qpochhammer_inf t x))"
    using G_sums[of t] t by (simp add: a_sol[unfolded a_def] f_def g_def)

  have "G = Abs_fps (λn. 1 / (k=1..n. 1 - x^k))"
    by (rule fps_ext) (simp add: a_sol[unfolded a_def])
  thus "g has_fps_expansion Abs_fps (λn. 1 / (k=1..n. 1 - x^k))"
    using G by simp
qed

lemma sums_inverse_qpochhammer_inf_real:
  assumes "¦x¦ < (1 :: real)" "¦t¦ < 1"
  shows   "(λn. t^n / (k=1..n. 1 - x^k)) sums inverse (qpochhammer_inf t x)"
proof -
  have "(λn. complex_of_real t ^ n / (k=1..n. 1 - of_real x ^ k)) 
          sums inverse (qpochhammer_inf (of_real t) (of_real x))" (is "?f sums ?S")
    by (intro sums_inverse_qpochhammer_inf_complex) (use assms in auto)
  also have "?f = (λn. complex_of_real (t ^ n / (k=1..n. 1 - x ^ k)))"
    by simp
  also have "inverse (qpochhammer_inf (of_real t) (of_real x)) = 
             complex_of_real (inverse (qpochhammer_inf t x))"
    by (subst qpochhammer_inf_of_real) (use assms in auto)
  finally show ?thesis
    by (subst (asm) sums_of_real_iff)
qed

lemma norm_summable_inverse_qpochhammer_inf:
  fixes x t :: "'a :: {real_normed_field}"
  assumes "norm x < 1" "norm t < 1"
  shows   "summable (λn. norm (t ^ n / (k=1..n. 1 - x^k)))"
proof (rule summable_comparison_test)
  show "summable (λn. norm t ^ n / (k=1..n. 1 - norm x ^ k))"
    by (rule sums_summable, rule sums_inverse_qpochhammer_inf_real) (use assms in auto)
next
  show "N. nN. norm (norm (t ^ n / (k = 1..n. 1 - x ^ k)))  
                  norm t ^ n / (k = 1..n. 1 - norm x ^ k)"
  proof (intro exI[of _ 0] allI impI)
    fix n :: nat
    have "norm (norm (t ^ n / (k=1..n. 1 - x ^ k))) = norm t ^ n / (k=1..n. norm (1 - x ^ k))"
      by (simp add: norm_mult norm_power norm_divide abs_prod flip:prod_norm)
    also have "  norm t ^ n / (k=1..n. 1 - norm x ^ k)"
    proof (intro divide_left_mono mult_pos_pos prod_pos prod_mono)
      fix k assume k: "k  {1..n}"
      have *: "0 < norm (1::'a) - norm (x ^ k)"
        using assms k by (simp add: norm_power power_less_one_iff)
      also have "  norm (1 - x ^ k)"
        by norm
      finally show "norm (1 - x ^ k) > 0" .
      from * show "1 - norm x ^ k > 0"
        by (simp add: norm_power)
      have "norm (1::'a) - norm (x ^ k)  norm (1 - x ^ k)"
        by norm
      thus "0  1 - norm x ^ k  1 - norm x ^ k  norm (1 - x ^ k)"
        using assms by (auto simp: norm_power power_le_one_iff)
    qed auto
    finally show "norm (norm (t ^ n / (k = 1..n. 1 - x ^ k)))
                   norm t ^ n / (k = 1..n. 1 - norm x ^ k)" .
  qed
qed


subsection ‹Euler's function›

text ‹
  Euler's $\phi$ function is closely related to the Dedekind $\eta$ function and the Jacobi
  $\vartheta$ nullwert functions. The $q$-Pochhammer symbol gives us a simple and convenient
  way to define it.
›
definition euler_phi :: "'a :: {real_normed_field, banach, heine_borel}  'a" where
  "euler_phi q = qpochhammer_inf q q"

lemma euler_phi_0 [simp]: "euler_phi 0 = 1"
  by (simp add: euler_phi_def)

lemma abs_convergent_euler_phi:
  assumes "(q :: 'a :: real_normed_div_algebra)  ball 0 1"
  shows   "abs_convergent_prod (λn. 1 - q ^ Suc n)"
proof (rule summable_imp_abs_convergent_prod)
  show "summable (λn. norm (1 - q ^ Suc n - 1))"
    using assms by (subst summable_Suc_iff) (auto simp: norm_power)
qed

lemma convergent_euler_phi:
  assumes "(q :: 'a :: {real_normed_field, banach})  ball 0 1"
  shows   "convergent_prod (λn. 1 - q ^ Suc n)"
  using abs_convergent_euler_phi[OF assms] abs_convergent_prod_imp_convergent_prod by blast

lemma has_prod_euler_phi:
  "norm q < 1  (λn. 1 - q ^ Suc n) has_prod euler_phi q"
  using has_prod_qpochhammer_inf[of q q] by (simp add: euler_phi_def)

lemma euler_phi_nonzero [simp]:
  assumes x: "x  ball 0 1"
  shows   "euler_phi x  0"
  using assms by (simp add: euler_phi_def qpochhammer_inf_nonzero)

lemma holomorphic_euler_phi [holomorphic_intros]:
  assumes [holomorphic_intros]: "f holomorphic_on A"
  assumes "z. z  A  norm (f z) < 1"
  shows   "(λz. euler_phi (f z)) holomorphic_on A"
proof -
  have *: "euler_phi holomorphic_on ball 0 1"
    unfolding euler_phi_def by (intro holomorphic_intros) auto
  show ?thesis
    by (rule holomorphic_on_compose_gen[OF assms(1) *, unfolded o_def]) (use assms(2) in auto)
qed

lemma analytic_euler_phi [analytic_intros]:
  assumes [analytic_intros]: "f analytic_on A"
  assumes "z. z  A  norm (f z) < 1"
  shows   "(λz. euler_phi (f z)) analytic_on A"
  using assms(2) by (auto intro!: analytic_intros simp: euler_phi_def)

lemma meromorphic_on_euler_phi [meromorphic_intros]:
  "f analytic_on A  (z. z  A  norm (f z) < 1)  (λz. euler_phi (f z)) meromorphic_on A"
  unfolding euler_phi_def by (intro meromorphic_intros)

lemma continuous_on_euler_phi [continuous_intros]:
  assumes "continuous_on A f" "z. z  A  norm (f z) < 1"
  shows   "continuous_on A (λz. euler_phi (f z))"
  using assms unfolding euler_phi_def by (intro continuous_intros) auto

lemma continuous_euler_phi [continuous_intros]:
  fixes a q :: "'b :: t2_space  'a :: {real_normed_field, banach, heine_borel}"
  assumes "continuous (at x within A) f" "norm (f x) < 1"
  shows   "continuous (at x within A) (λx. euler_phi (f x))"
  unfolding euler_phi_def by (intro continuous_intros assms)

lemma tendsto_euler_phi [tendsto_intros]:
  assumes [tendsto_intros]: "(f  c) F" and "norm c < 1"
  shows   "((λx. euler_phi (f x))  euler_phi c) F"
  unfolding euler_phi_def using assms by (auto intro!: tendsto_intros)

end