/-- 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.
    rintro ⟨a, b⟩
    rw [sigma_composition_eq_iff]
    dsimp
    constructor
    · conv_rhs =>
        rw [← join_splitWrtComposition a.blocks b, ← ofFn_get (splitWrtComposition a.blocks b)]
      have A : length (gather a b) = List.length (splitWrtComposition a.blocks b) := by
        simp only [length, gather, length_map, length_splitWrtComposition]
      congr! 2
      · exact (Fin.heq_fun_iff A (α := List ℕ)).2 fun i => rfl
    · have B : Composition.length (Composition.gather a b) = List.length b.blocks :=
        Composition.length_gather _ _
      conv_rhs => rw [← ofFn_get b.blocks]
      congr 1
      refine' (Fin.heq_fun_iff B).2 fun i => _
      rw [sigmaCompositionAux, Composition.length, List.get_map_rev List.length,
        List.get_of_eq (map_length_splitWrtComposition _ _)]
  right_inv := by
    -- the fact that we have a right inverse is essentially `splitWrtComposition_join`,
    -- but we need to massage it to take care of the dependent setting.
    rintro ⟨c, d⟩
    have : map List.sum (ofFn fun i : Fin (Composition.length c) => (d i).blocks) = c.blocks := by
      simp [map_ofFn, (· ∘ ·), Composition.blocks_sum, Composition.ofFn_blocksFun]
    rw [sigma_pi_composition_eq_iff]
    dsimp
    congr! 1
    · congr
      ext1
      dsimp [Composition.gather]
      rwa [splitWrtComposition_join]
      simp only [map_ofFn]
      rfl
    · rw [Fin.heq_fun_iff]
      intro i
      dsimp [Composition.sigmaCompositionAux]
      rw [get_of_eq (splitWrtComposition_join _ _ _)]
      · simp only [get_ofFn]
        

Proof state:
case mk.h.e'_3
𝕜 : 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 : ℕ
c : Composition n
d : (i : Fin (length c)) → Composition (blocksFun c i)
this : List.map sum (ofFn fun i => (d i).blocks) = c.blocks
e_2✝ :
  length
      (gather
        { blocks := List.join (ofFn fun j => (d j).blocks),
          blocks_pos := (_ : ∀ {i : ℕ}, i ∈ List.join (ofFn fun j => (d j).blocks) → 0 < i),
          blocks_sum := (_ : sum (List.join (ofFn fun j => (d j).blocks)) = n) }
        { blocks := ofFn fun j => length (d j), blocks_pos := (_ : ∀ {k : ℕ}, (k ∈ ofFn fun j => length (d j)) → 0 < k),
          blocks_sum :=
            (_ :
              sum (ofFn fun j => List.length (d j).blocks) = List.length (List.join (ofFn fun j => (d j).blocks))) }) =
    length c
i :
  Fin
    (length
      (gather
        { blocks := List.join (ofFn fun j => (d j).blocks),
          blocks_pos := (_ : ∀ {i : ℕ}, i ∈ List.join (ofFn fun j => (d j).blocks) → 0 < i),
          blocks_sum := (_ : sum (List.join (ofFn fun j => (d j).blocks)) = n) }
        { blocks := ofFn fun j => length (d j), blocks_pos := (_ : ∀ {k : ℕ}, (k ∈ ofFn fun j => length (d j)) → 0 < k),
          blocks_sum :=
            (_ :
              sum (ofFn fun j => List.length (d j).blocks) = List.length (List.join (ofFn fun j => (d j).blocks))) }))
⊢ (d
        (Fin.cast (_ : List.length (ofFn fun j => (d j).blocks) = length c)
          { val := ↑i, isLt := (_ : ↑i < List.length (ofFn fun j => (d j).blocks)) })).blocks =
    (d { val := ↑i, isLt := (_ : ↑i < length c) }).blocks

Next tactic:
rfl