Covering counts and degree bound

The covering Faà di Bruno formula E0006 counts its own terms and bounds the degree of composites.

(1) Proposition (Covering counts). Let \(g(x) = 2^x - 1\) and \(f(y) = 2^y\) on \(\IN_0\). Then, for \(k \geq 1\),

\[ |\Cov(k)| = \sum_{j=0}^{k} (-1)^{k-j} \binom{k}{j}\, 2^{2^j - 1}. \]

The sequence begins \(1, 5, 109, 32297, \dots\) (OEIS A003465).

Proof. Every increment equals \(1\): \(\Delta^j(g; 0) = 1\) for \(j \geq 1\) and \(\Delta^p(f; 0) = 1\) for \(p \geq 1\) at \(y = g(0) = 0\). The covering formula E0006 gives \(\Delta^k(f \circ g; 0) = \sum_{H \in \Cov(k)} 1 = |\Cov(k)|\); the monoid domain \(\IN_0\) is admissible since all evaluation points are nonnegative E0001. On the other hand, the alternating-sum formula for \(\Delta^k\) E0004 evaluates \(\Delta^k(f \circ g; 0) = \sum_{j=0}^{k} (-1)^{k-j} \binom{k}{j}\, (f \circ g)(j)\) with \((f \circ g)(j) = 2^{2^j - 1}\).

(2) Corollary (Degree bound). A map \(g: X \to Y\) between abelian groups is polynomial of degree \(\leq d\) if \(\Delta^{d+1} g \equiv 0\). If \(g\) has degree \(\leq d\) and \(f: Y \to Z\) has degree \(\leq e\), then \(f \circ g\) has degree \(\leq ed\).

Proof. Set \(k = ed + 1\) and take any point and directions. In the covering formula E0006, each \(H \in \Cov(k)\) either contains a block with \(|T| > d\) — then the direction \(\Delta(g; x; u_T) = 0\), and a zero direction annihilates the iterated difference E0004 — or has all blocks of size \(\leq d\); in the latter case \(|H| \leq e\) would give \(|{\textstyle\bigcup} H| \leq \sum_{T \in H} |T| \leq ed < k\), contradicting \(\bigcup H = [k]\), so \(|H| \geq e + 1\) and \(\Delta^{|H|} f = \Delta^{|H|-e-1} \Delta^{e+1} f \equiv 0\). Every term vanishes, hence \(\Delta^{ed+1}(f \circ g) = 0\).


(3) Validation (AI review, 2026-07-19, claude-fable-5, pass). Numeric checks \(k = 1, 2, 3\) (\(1, 5, 109\); \(k=2\) by hand enumeration); increment values \(\Delta^j g(0) = \Delta^p f(0) = 1\); degree-bound dichotomy incl. \(d = 0\), \(e = 0\). Fixed: added missing \(k \geq 1\) (formula gives \(1\) at \(k=0\) but \(\Cov(0) = \emptyset\)), made the alternating-sum step and the zero-direction/block-count dichotomy explicit, added E0004 to depends_on.


Lean formalization (source)
import Elements.E0006
import Elements.E0008

/-!
# Covering counts and degree bound  (E0019)

Formalises `Prop:cover-count` and `Cor:degree-bound` from
*Discrete Faà di Bruno via Möbius Inversion* (Hartmann, 2026):

  `|Cov(k)| = ∑_{j=0}^{k} (-1)^{k-j} C(k,j) 2^{2^j - 1}`   (OEIS A003465).

The proof is the paper's: apply the covering FdB formula to
`g(x) = 2^x - 1` and `f(y) = 2^y`.  Every forward difference of `g`
(order ≥ 1) and of `f` at the relevant points equals `1`, so the
covering formula reads `Δ^k(f∘g)(0) = |Cov(k)|`; expanding the left
side as a Newton sum gives the stated identity.  The covering formula
counts its own terms — the discrete parallel of `e^{e^x-1}` counting
partitions.

The degree bound: a map is *polynomial of degree ≤ d* if `Δ^{d+1} ≡ 0`;
combined with the covering FdB and the weight bound (E0008), degrees
compose multiplicatively: `deg(f ∘ g) ≤ deg f · deg g`.
-/

open Finset

namespace Elements

variable {β : Type*} [DecidableEq β]

/-- **Newton form** of the forward difference with unit directions:
`Δ(h; x; 1,…,1; S) = ∑_{j≤k} (-1)^{k-j} C(k,j) h(x+j)` with `k = |S|`. -/
lemma fwdDiff_unit_dirs (h : ℤ → ℤ) (x : ℤ) (u : β → ℤ) (S : Finset β)
    (hu : ∀ i ∈ S, u i = 1) :
    fwdDiff h x u S
      = ∑ j ∈ Finset.range (S.card + 1),
          (-1 : ℤ) ^ (S.card - j) * (S.card.choose j) * h (x + j) := by
  unfold fwdDiff mu tr
  have hsummand : ∀ T ∈ S.powerset,
      ((-1 : ℤ) ^ (S.card - T.card)) • h (x + ∑ i ∈ T, u i)
        = (-1 : ℤ) ^ (S.card - T.card) * h (x + T.card) := by
    intro T hT
    rw [Finset.mem_powerset] at hT
    rw [smul_eq_mul]
    congr 2
    rw [Finset.sum_congr rfl (fun i hi => hu i (hT hi)), Finset.sum_const,
        nsmul_eq_mul, mul_one]
  rw [Finset.sum_congr rfl hsummand, Finset.powerset_card_disjiUnion,
      Finset.sum_disjiUnion]
  refine Finset.sum_congr rfl ?_
  intro j _
  have hconst : ∀ T ∈ Finset.powersetCard j S,
      (-1 : ℤ) ^ (S.card - T.card) * h (x + T.card)
        = (-1 : ℤ) ^ (S.card - j) * h (x + j) := by
    intro T hT
    rw [(Finset.mem_powersetCard.mp hT).2]
  rw [Finset.sum_congr rfl hconst, Finset.sum_const, Finset.card_powersetCard,
      nsmul_eq_mul]
  ring

/-- `∑_{j≤k} (-1)^{k-j} C(k,j) b^j = (b-1)^k` — the Newton/binomial identity. -/
lemma newton_binomial (b : ℤ) (k : ℕ) :
    (∑ j ∈ Finset.range (k + 1), (-1 : ℤ) ^ (k - j) * (k.choose j) * b ^ j)
      = (b - 1) ^ k := by
  have h := add_pow b (-1) k
  rw [show b + -1 = b - 1 from by ring] at h
  rw [h]
  refine Finset.sum_congr rfl ?_
  intro j _
  ring

/-- Forward differences of `g(x) = 2^x - 1` with unit directions: every
increment of order ≥ 1 equals `1` (this is `(2-1)^k - (1-1)^k = 1`). -/
lemma fwdDiff_two_pow_sub_one (u : β → ℤ) (A : Finset β) (hA : A.Nonempty)
    (hu : ∀ i ∈ A, u i = 1) :
    fwdDiff (fun n : ℤ => (2 : ℤ) ^ n.toNat - 1) 0 u A = 1 := by
  rw [fwdDiff_unit_dirs _ _ _ _ hu]
  have hval : ∀ j ∈ Finset.range (A.card + 1),
      (-1 : ℤ) ^ (A.card - j) * (A.card.choose j)
          * ((fun n : ℤ => (2 : ℤ) ^ n.toNat - 1) (0 + j))
        = (-1 : ℤ) ^ (A.card - j) * (A.card.choose j) * 2 ^ j
          - (-1 : ℤ) ^ (A.card - j) * (A.card.choose j) * 1 ^ j := by
    intro j _
    simp only [zero_add, Int.toNat_natCast]
    ring
  rw [Finset.sum_congr rfl hval, Finset.sum_sub_distrib,
      newton_binomial, newton_binomial]
  have hk : A.card ≠ 0 := Finset.card_ne_zero.mpr hA
  norm_num [zero_pow hk]

/-- Forward differences of `f(y) = 2^y` with unit directions: every
increment equals `1` (this is `(2-1)^k = 1`, valid for all orders). -/
lemma fwdDiff_two_pow {γ : Type*} [DecidableEq γ] (v : γ → ℤ) (C : Finset γ)
    (hv : ∀ A ∈ C, v A = 1) :
    fwdDiff (fun n : ℤ => (2 : ℤ) ^ n.toNat) 0 v C = 1 := by
  rw [fwdDiff_unit_dirs _ _ _ _ hv]
  have hval : ∀ j ∈ Finset.range (C.card + 1),
      (-1 : ℤ) ^ (C.card - j) * (C.card.choose j)
          * ((fun n : ℤ => (2 : ℤ) ^ n.toNat) (0 + j))
        = (-1 : ℤ) ^ (C.card - j) * (C.card.choose j) * 2 ^ j := by
    intro j _
    simp only [zero_add, Int.toNat_natCast]
  rw [Finset.sum_congr rfl hval, newton_binomial]
  norm_num

/-- **Covering counts** (`Prop:cover-count`).

`|Cov(S)| = ∑_{j=0}^{k} (-1)^{k-j} C(k,j) 2^{2^j - 1}` with `k = |S|`
(OEIS A003465: 1, 5, 109, 32297, …).

Proof: the covering FdB formula applied to `g(x) = 2^x - 1`, `f(y) = 2^y`
has every covering term equal to `1`, so it counts `|Cov(S)|`; the Newton
expansion of `Δ^k(f∘g)(0)` gives the alternating sum. -/
theorem cover_count {α : Type*} [DecidableEq α] (S : Finset α) (hS : S.Nonempty) :
    ((coverings S).card : ℤ)
      = ∑ j ∈ Finset.range (S.card + 1),
          (-1 : ℤ) ^ (S.card - j) * (S.card.choose j) * 2 ^ (2 ^ j - 1) := by
  have h := covering_fdb (fun n : ℤ => (2 : ℤ) ^ n.toNat) (fun n : ℤ => (2 : ℤ) ^ n.toNat - 1)
              0 (fun _ => (1 : ℤ)) hS
  -- every covering term equals 1
  have hterm : ∀ C ∈ coverings S,
      fwdDiff (fun n : ℤ => (2 : ℤ) ^ n.toNat)
        ((fun n : ℤ => (2 : ℤ) ^ n.toNat - 1) 0)
        (fwdDiff (fun n : ℤ => (2 : ℤ) ^ n.toNat - 1) 0 (fun _ => (1 : ℤ))) C = 1 := by
    intro C hC
    have hg0 : (fun n : ℤ => (2 : ℤ) ^ n.toNat - 1) 0 = 0 := by norm_num
    rw [hg0]
    refine fwdDiff_two_pow _ _ ?_
    intro A hA
    have hApow : A ∈ powPlus S :=
      Finset.mem_powerset.mp (Finset.mem_filter.mp hC).1 hA
    exact fwdDiff_two_pow_sub_one _ _ (mem_powPlus.mp hApow).1 (fun _ _ => rfl)
  rw [Finset.sum_congr rfl hterm, Finset.sum_const, nsmul_eq_mul, mul_one] at h
  -- expand the left side as a Newton sum
  rw [fwdDiff_unit_dirs _ _ _ _ (fun _ _ => rfl)] at h
  rw [← h]
  refine Finset.sum_congr rfl ?_
  intro j _
  -- (f ∘ g)(j) = 2^(2^j - 1)
  simp only [Function.comp_apply, zero_add, Int.toNat_natCast]
  congr 1
  -- ((2:ℤ)^j - 1).toNat = 2^j - 1  (as ℕ)
  have h1 : ((2 : ℤ) ^ j - 1) = ((2 ^ j - 1 : ℕ) : ℤ) := by
    push_cast [Nat.one_le_two_pow]
    ring
  rw [h1, Int.toNat_natCast]

/-- Sanity check: `|Cov(2)| = 5` (the five covers of `{0,1}`:
`{01}`, `{0,1}`, `{0,01}`, `{1,01}`, `{0,1,01}`). -/
example : (coverings (Finset.univ : Finset (Fin 2))).card = 5 := by decide

/-- Sanity check: `|Cov(1)| = 1`. -/
example : (coverings (Finset.univ : Finset (Fin 1))).card = 1 := by decide

section DegreeBound

variable {α : Type*} [DecidableEq α]
         {X : Type*} [AddCommGroup X]
         {Y : Type*} [AddCommGroup Y]
         {Z : Type*} [AddCommGroup Z]

/-! ### Forward differences with a zero direction vanish -/

/-- If one direction is zero, the forward difference vanishes.
The cube doesn't depend on whether `i ∈ T` (since `v i = 0`),
so the `±1` contributions from `T` and `T Δ {i}` cancel pairwise. -/
lemma fwdDiff_zero_dir (f : X → Y) (x : X) (v : β → X) {S : Finset β} {i : β}
    (hi : i ∈ S) (hv : v i = 0) :
    fwdDiff f x v S = 0 := by
  unfold fwdDiff mu
  -- Pair T with T Δ {i}: the cube values are equal (v i = 0) and signs cancel
  refine Finset.sum_involution (fun T _ => if i ∈ T then T.erase i else insert i T)
    ?_ ?_ ?_ ?_
  · -- Signs cancel: f(T) + f(toggle T) = 0
    intro T hT
    split <;> rename_i h
    · have hval : tr f x v (T.erase i) = tr f x v T := by
        unfold tr; congr 1; rw [Finset.sum_erase_eq_sub h, hv, sub_zero]
      have hpos : 0 < T.card := Finset.card_pos.mpr ⟨i, h⟩
      have hle : T.card ≤ S.card := Finset.card_le_card (Finset.mem_powerset.mp hT)
      rw [Finset.card_erase_of_mem h,
          show S.card - (T.card - 1) = (S.card - T.card) + 1 from by omega,
          pow_succ, mul_neg_one, neg_smul, hval, add_neg_cancel]
    · have hval : tr f x v (insert i T) = tr f x v T := by
        unfold tr; congr 1; rw [Finset.sum_insert h, hv, zero_add]
      have hlt : T.card < S.card :=
        Finset.card_lt_card ⟨Finset.mem_powerset.mp hT, fun hle => h (hle hi)⟩
      rw [Finset.card_insert_of_notMem h,
          show S.card - T.card = (S.card - (T.card + 1)) + 1 from by omega,
          pow_succ, mul_neg_one, neg_smul, hval, neg_add_cancel]
  · -- Toggle ≠ identity
    intro T _ _
    split <;> rename_i h
    · intro he
      have h1 := Finset.card_erase_of_mem h
      have h2 := Finset.card_pos.mpr ⟨i, h⟩
      have h3 : (T.erase i).card = T.card := by rw [he]
      omega
    · intro he
      have h1 := Finset.card_insert_of_notMem h
      have h3 : (insert i T).card = T.card := by rw [he]
      omega
  · -- Toggle maps into S.powerset
    intro T hT
    rw [Finset.mem_powerset] at hT ⊢
    split <;> rename_i h
    · exact (Finset.erase_subset i T).trans hT
    · exact Finset.insert_subset hi hT
  · -- Toggle is an involution
    intro T _
    split <;> rename_i h
    · rw [if_neg (fun h' => (Finset.mem_erase.mp h').1 rfl), Finset.insert_erase h]
    · rw [if_pos (Finset.mem_insert_self i T), Finset.erase_insert h]

/-! ### Polynomial maps -/

/-- A map is *polynomial of degree ≤ d* if all forward differences of
order > d vanish.  Degree 0 means constant, degree 1 means affine, etc. -/
def IsPolyDeg (f : X → Y) (d : ℕ) : Prop :=
  ∀ (x : X) (u : α → X) (S : Finset α), d < S.card → fwdDiff f x u S = 0

/-! ### Degree bound -/

/-- **Degree bound** (`Cor:degree-bound`).
If `g : X → Y` is polynomial of degree ≤ d and `f : Y → Z` is polynomial
of degree ≤ e (both with respect to index type `Finset α`), then
`f ∘ g` is polynomial of degree ≤ `e * d`.

The proof uses the covering FdB: each covering term involves `Δ^{|C|}f`
(vanishing for `|C| > e`) applied to directions `Δ^{|A|}g` (vanishing
for `|A| > d`), and `|S| ≤ ∑|A| ≤ e·d` by the weight bound. -/
theorem degree_bound {d e : ℕ}
    (f : Y → Z) (g : X → Y)
    (hg : ∀ x (u : α → X) (S : Finset α), d < S.card → fwdDiff g x u S = 0)
    (hf : ∀ y (v : Finset α → Y) (C : Finset (Finset α)), e < C.card → fwdDiff f y v C = 0) :
    ∀ x (u : α → X) (S : Finset α), e * d < S.card → fwdDiff (f ∘ g) x u S = 0 := by
  intro x u S hS
  by_cases hSne : S.Nonempty
  · -- Apply covering FdB
    rw [covering_fdb f g x u hSne]
    apply Finset.sum_eq_zero
    intro C hC
    by_cases he' : e < C.card
    · exact hf (g x) (fwdDiff g x u) C he'
    · push_neg at he'
      have : ¬∀ A ∈ C, A.card ≤ d :=
        fun hall => absurd hS (not_lt.mpr (cover_size_bound hC he' hall))
      push_neg at this
      obtain ⟨A, hA, hAd⟩ := this
      exact fwdDiff_zero_dir f (g x) (fwdDiff g x u) hA (hg x u A hAd)
  · simp [Finset.not_nonempty_iff_eq_empty.mp hSne] at hS

end DegreeBound

end Elements