/-- Natural equivalence between `(Σ (a : composition n), composition a.length)` and
`(Σ (c : composition n), Π (i : fin c.length), composition (c.blocks_fun i))`, that shows up as a
change of variables in the proof that composition of formal multilinear series is associative.

Consider a composition `a` of `n` and a composition `b` of `a.length`. Then `b` indicates how to
group together some blocks of `a`, giving altogether `b.length` blocks of blocks. These blocks of
blocks can be called `d₀, ..., d_{a.length - 1}`, and one obtains a composition `c` of `n` by
saying that each `dᵢ` is one single block. The map `⟨a, b⟩ → ⟨c, (d₀, ..., d_{a.length - 1})⟩` is
the direct map in the equiv.

Conversely, if one starts from `c` and the `dᵢ`s, one can join the `dᵢ`s to obtain a composition
`a` of `n`, and register the lengths of the `dᵢ`s in a composition `b` of `a.length`. This is the
inverse map of the equiv.
-/
def sigmaEquivSigmaPi (n : ℕ) :
    (Σ a : Composition n, Composition a.length) ≃
      Σ c : Composition n, ∀ i : Fin c.length, Composition (c.blocksFun i) where
  toFun i := ⟨i.1.gather i.2, i.1.sigmaCompositionAux i.2⟩
  invFun i :=
    ⟨{  blocks := (ofFn fun j => (i.2 j).blocks).join
        blocks_pos := by
          simp only [and_imp, List.mem_join, exists_imp, forall_mem_ofFn_iff]
          exact @fun i j hj => Composition.blocks_pos _ hj
        blocks_sum := by simp [sum_ofFn, Composition.blocks_sum, Composition.sum_blocksFun] },
      { blocks := ofFn fun j => (i.2 j).length
        blocks_pos := by
          intro k hk
          refine' ((forall_mem_ofFn_iff (P := fun i => 0 < i)).2 fun j => _) k hk
          exact Composition.length_pos_of_pos _ (Composition.blocks_pos' _ _ _)
        blocks_sum := by dsimp only [Composition.length]; simp [sum_ofFn] }⟩
  left_inv := by
    -- the fact that we have a left inverse is essentially `join_split_wrt_composition`,
    -- but we need to massage it to take care of the dependent setting.
    

Proof state:
𝕜 : Type u_1
E : Type u_2
F : Type u_3
G : Type u_4
H : Type u_5
inst✝⁸ : NontriviallyNormedField 𝕜
inst✝⁷ : NormedAddCommGroup E
inst✝⁶ : NormedSpace 𝕜 E
inst✝⁵ : NormedAddCommGroup F
inst✝⁴ : NormedSpace 𝕜 F
inst✝³ : NormedAddCommGroup G
inst✝² : NormedSpace 𝕜 G
inst✝¹ : NormedAddCommGroup H
inst✝ : NormedSpace 𝕜 H
n✝ n : ℕ
⊢ Function.LeftInverse
    (fun i =>
      {
        fst :=
          { blocks := List.join (ofFn fun j => (Sigma.snd i j).blocks),
            blocks_pos := (_ : ∀ {i_1 : ℕ}, i_1 ∈ List.join (ofFn fun j => (Sigma.snd i j).blocks) → 0 < i_1),
            blocks_sum := (_ : sum (List.join (ofFn fun j => (Sigma.snd i j).blocks)) = n) },
        snd :=
          { blocks := ofFn fun j => length (Sigma.snd i j),
            blocks_pos := (_ : ∀ {k : ℕ}, (k ∈ ofFn fun j => length (Sigma.snd i j)) → 0 < k),
            blocks_sum :=
              (_ :
                sum (ofFn fun j => length (Sigma.snd i j)) =
                  length
                    { blocks := List.join (ofFn fun j => (Sigma.snd i j).blocks),
                      blocks_pos := (_ : ∀ {i_1 : ℕ}, i_1 ∈ List.join (ofFn fun j => (Sigma.snd i j).blocks) → 0 < i_1),
                      blocks_sum := (_ : sum (List.join (ofFn fun j => (Sigma.snd i j).blocks)) = n) }) } })
    fun i => { fst := gather i.fst i.snd, snd := sigmaCompositionAux i.fst i.snd }

Next tactic:
rintro ⟨a, b⟩