docs: def check_constraint_convergence(X, L, LX, Z, U, R, S, step_f, step_g, e_rel, e_abs):
      """Calculate if all constraints have converged.
  
      Using the stopping criteria from Boyd 2011, Sec 3.3.1, calculate whether the
      variables for each constraint have converged.
      """
  
      if isinstance(L, list):
          M = len(L)
          convergence = True
          errors = []
          # recursive call
          for i in range(M):
              c, e = check_constraint_convergence(X, L[i], LX[i], Z[i], U[i], R[i], S[i],
                                                  step_f, step_g[i], e_rel, e_abs)
              convergence &= c
              errors.append(e)
          return convergence, errors
      else:
          # check convergence of prime residual R and dual residual S
          e_pri, e_dual = get_variable_errors(X, L, LX, Z, U, step_g, e_rel, e_abs)
          lR = l2(R)
          lS = l2(S)
          convergence = (lR <= e_pri) and (lS <= e_dual)
          return convergence, (e_pri, e_dual, lR, lS)