docs: def update_variables(X, Z, U, prox_f, step_f, prox_g, step_g, L):
      """Update the primal and dual variables
  
      Note: X, Z, U are updated inline
  
      Returns: LX, R, S
      """
      if not hasattr(prox_g, '__iter__'):
          if prox_g is not None:
              dX = step_f/step_g * L.T.dot(L.dot(X) - Z + U)
              X[:] = prox_f(X - dX, step_f)
              LX, R, S = do_the_mm(X, step_f, Z, U, prox_g, step_g, L)
          else:
              # fall back to simple fixed-point method for f
              # see do_the_mm for normal definitions of LX,Z,R,S
              S = -X.copy()
              X[:] = prox_f(X, step_f)
              LX = X
              Z[:] = X[:]
              R = np.zeros(X.shape, dtype=X.dtype)
              S += X
  
      else:
          M = len(prox_g)
          dX = np.sum([step_f/step_g[i] * L[i].T.dot(L[i].dot(X) - Z[i] + U[i]) for i in range(M)], axis=0)
          X[:] = prox_f(X - dX, step_f)
          LX = [None] * M
          R = [None] * M
          S = [None] * M
          for i in range(M):
              LX[i], R[i], S[i] = do_the_mm(X, step_f, Z[i], U[i], prox_g[i], step_g[i], L[i])
      return LX, R, S