/-- Auxiliary lemma to prove that the composition of formal multilinear series is associative.

Consider a composition `a` of `n` and a composition `b` of `a.length`. Grouping together some
blocks of `a` according to `b` as in `a.gather b`, one can compute the total size of the blocks
of `a` up to an index `size_up_to b i + j` (where the `j` corresponds to a set of blocks of `a`
that do not fill a whole block of `a.gather b`). The first part corresponds to a sum of blocks
in `a.gather b`, and the second one to a sum of blocks in the next block of
`sigma_composition_aux a b`. This is the content of this lemma. -/
theorem sizeUpTo_sizeUpTo_add (a : Composition n) (b : Composition a.length) {i j : ℕ}
    (hi : i < b.length) (hj : j < blocksFun b ⟨i, hi⟩) :
    sizeUpTo a (sizeUpTo b i + j) =
      sizeUpTo (a.gather b) i +
        sizeUpTo (sigmaCompositionAux a b ⟨i, (length_gather a b).symm ▸ hi⟩) j := by
  -- Porting note: `induction'` left a spurious `hj` in the context
  induction j with
  | zero =>
    show
      sum (take (b.blocks.take i).sum a.blocks) =
        sum (take i (map sum (splitWrtComposition a.blocks b)))
    induction' i with i IH
    · rfl
    · have A : i < b.length := Nat.lt_of_succ_lt hi
      have B : i < List.length (map List.sum (splitWrtComposition a.blocks b)) := by simp [A]
      have C : 0 < blocksFun b ⟨i, A⟩ := Composition.blocks_pos' _ _ _
      rw [sum_take_succ _ _ B, ← IH A C]
      have :
        take (sum (take i b.blocks)) a.blocks =
          take (sum (take i b.blocks)) (take (sum (take (i + 1) b.blocks)) a.blocks) := by
        rw [take_take, min_eq_left]
        apply monotone_sum_take _ (Nat.le_succ _)
      rw [this, get_map, get_splitWrtComposition, ←
        take_append_drop (sum (take i b.blocks)) (take (sum (take (Nat.succ i) b.blocks)) a.blocks),
        sum_append]
      congr
      rw [take_append_drop]
  | succ j IHj =>
    have A : j < blocksFun b ⟨i, hi⟩ := lt_trans (lt_add_one j) hj
    have B : j < length (sigmaCompositionAux a b ⟨i, (length_gather a b).symm ▸ hi⟩) := by
      convert A; rw [← length_sigmaCompositionAux]
    have C : sizeUpTo b i + j < sizeUpTo b (i + 1) := by
      simp only [sizeUpTo_succ b hi, add_lt_add_iff_left]
      exact A
    have D : sizeUpTo b i + j < length a := lt_of_lt_of_le C (b.sizeUpTo_le _)
    have : sizeUpTo b i + Nat.succ j = (sizeUpTo b i + j).succ := rfl
    rw [this, sizeUpTo_succ _ D, IHj A, sizeUpTo_succ _ B]
    simp only [sigmaCompositionAux, add_assoc, add_left_inj, Fin.val_mk]
    

Proof state:
case succ
𝕜 : 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 : ℕ
a : Composition n
b : Composition (length a)
i : ℕ
hi : i < length b
j : ℕ
IHj :
  j < blocksFun b { val := i, isLt := hi } →
    sizeUpTo a (sizeUpTo b i + j) =
      sizeUpTo (gather a b) i + sizeUpTo (sigmaCompositionAux a b { val := i, isLt := (_ : i < length (gather a b)) }) j
hj : Nat.succ j < blocksFun b { val := i, isLt := hi }
A : j < blocksFun b { val := i, isLt := hi }
B : j < length (sigmaCompositionAux a b { val := i, isLt := (_ : i < length (gather a b)) })
C : sizeUpTo b i + j < sizeUpTo b (i + 1)
D : sizeUpTo b i + j < length a
this : sizeUpTo b i + Nat.succ j = Nat.succ (sizeUpTo b i + j)
⊢ sizeUpTo (gather a b) i +
      (sizeUpTo
          {
            blocks :=
              List.get (splitWrtComposition a.blocks b)
                { val := i,
                  isLt :=
                    (_ :
                      ↑{ val := i, isLt := (_ : i < length (gather a b)) } <
                        List.length (splitWrtComposition a.blocks b)) },
            blocks_pos :=
              (_ :
                ∀ {i_1 : ℕ},
                  i_1 ∈
                      List.get (splitWrtComposition a.blocks b)
                        { val := ↑{ val := i, isLt := (_ : i < length (gather a b)) },
                          isLt :=
                            (_ :
                              ↑{ val := i, isLt := (_ : i < length (gather a b)) } <
                                List.length (splitWrtComposition a.blocks b)) } →
                    0 < i_1),
            blocks_sum :=
              (_ :
                sum
                    (List.get (splitWrtComposition a.blocks b)
                      { val := ↑{ val := i, isLt := (_ : i < length (gather a b)) },
                        isLt :=
                          (_ :
                            ↑{ val := i, isLt := (_ : i < length (gather a b)) } <
                              List.length (splitWrtComposition a.blocks b)) }) =
                  List.get (List.map sum (splitWrtComposition a.blocks b))
                    { val := i, isLt := (_ : i < length (gather a b)) }) }
          j +
        List.get a.blocks { val := sizeUpTo b i + j, isLt := D }) =
    sizeUpTo (gather a b) i +
      (sizeUpTo
          {
            blocks :=
              List.get (splitWrtComposition a.blocks b)
                { val := i,
                  isLt :=
                    (_ :
                      ↑{ val := i, isLt := (_ : i < length (gather a b)) } <
                        List.length (splitWrtComposition a.blocks b)) },
            blocks_pos :=
              (_ :
                ∀ {i_1 : ℕ},
                  i_1 ∈
                      List.get (splitWrtComposition a.blocks b)
                        { val := ↑{ val := i, isLt := (_ : i < length (gather a b)) },
                          isLt :=
                            (_ :
                              ↑{ val := i, isLt := (_ : i < length (gather a b)) } <
                                List.length (splitWrtComposition a.blocks b)) } →
                    0 < i_1),
            blocks_sum :=
              (_ :
                sum
                    (List.get (splitWrtComposition a.blocks b)
                      { val := ↑{ val := i, isLt := (_ : i < length (gather a b)) },
                        isLt :=
                          (_ :
                            ↑{ val := i, isLt := (_ : i < length (gather a b)) } <
                              List.length (splitWrtComposition a.blocks b)) }) =
                  List.get (List.map sum (splitWrtComposition a.blocks b))
                    { val := i, isLt := (_ : i < length (gather a b)) }) }
          j +
        List.get
          (List.get (splitWrtComposition a.blocks b)
            { val := i,
              isLt :=
                (_ :
                  ↑{ val := i, isLt := (_ : i < length (gather a b)) } <
                    List.length (splitWrtComposition a.blocks b)) })
          { val := j, isLt := B })

Next tactic:
rw [get_of_eq (get_splitWrtComposition _ _ _), get_drop', get_take _ _ C]