code: public static float[] computeUpdatedXu(Solver solver,
                                           double value,
                                           float[] Xu,
                                           float[] Yi,
                                           boolean implicit) {
      if (Yi == null) {
        return null;
      }
  
      boolean noXu = Xu == null;
      double Qui = noXu ? 0.0 : VectorMath.dot(Xu, Yi);
      // Qui' is the target, new value of Qui
      // 0.5 reflects a "don't know" state
      double targetQui = computeTargetQui(implicit, value, noXu ? 0.5 : Qui);
      if (Double.isNaN(targetQui)) {
        return null;
      }
  
      double dQui = targetQui - Qui;
      double[] dQuiYi = new double[Yi.length];
      for (int i = 0; i < dQuiYi.length; i++) {
        dQuiYi[i] = Yi[i] * dQui;
      }
      double[] dXu = solver.solveDToD(dQuiYi);
  
      float[] newXu = noXu ? new float[dXu.length] : Xu.clone();
      for (int i = 0; i < newXu.length; i++) {
        newXu[i] += dXu[i];
      }
      return newXu;
    }
docs: Computes how a user vector Xu changes in response to interaction with an item vector Yi.
  This can also be used to compute how an item vector changes in response to a user interaction,
  even though the code naming follows the former convention.
  
  @param solver solver helping solve for Xu in Qu*Y = Xu * (Yt * Y)
  @param value strength of interaction
  @param Xu current user vector (null if no existing user vector)
  @param Yi current item vector
  @param implicit whether the model is implicit feedback
  @return new user vector Xu, or {@code null} if no update should be made (i.e. there was no
  item vector; the update would push the new Qui farther out of range)