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 = (∏k≤0. 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~\<^cite>‹andrews2004›.
›
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~\<^cite>‹andrews2004›. 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 "∃m≥n+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. ∀n≥N. 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