public static FastByIDMap<FastIDSet> toDataMap(FastByIDMap<PreferenceArray> data) {
   for (Map.Entry<Long, Object> entry : ((FastByIDMap<Object>) (FastByIDMap<?>) data).entrySet()) {
     PreferenceArray prefArray = (PreferenceArray) entry.getValue();
     int size = prefArray.length();
     FastIDSet itemIDs = new FastIDSet(size);
     for (int i = 0; i < size; i++) {
       itemIDs.add(prefArray.getItemID(i));
     }
     entry.setValue(itemIDs);
   }
   return (FastByIDMap<FastIDSet>) (FastByIDMap<?>) data;
 }
 private void buildAverageDiffs() throws TasteException {
   try {
     buildAveragesLock.writeLock().lock();
     DataModel dataModel = getDataModel();
     LongPrimitiveIterator it = dataModel.getUserIDs();
     while (it.hasNext()) {
       long userID = it.nextLong();
       PreferenceArray prefs = dataModel.getPreferencesFromUser(userID);
       int size = prefs.length();
       for (int i = 0; i < size; i++) {
         long itemID = prefs.getItemID(i);
         float value = prefs.getValue(i);
         addDatumAndCreateIfNeeded(itemID, value, itemAverages);
         addDatumAndCreateIfNeeded(userID, value, userAverages);
         overallAveragePrefValue.addDatum(value);
       }
     }
   } finally {
     buildAveragesLock.writeLock().unlock();
   }
 }
  @Override
  public double userSimilarity(long userID1, long userID2) throws TasteException {
    PreferenceArray xPrefs = dataModel.getPreferencesFromUser(userID1);
    PreferenceArray yPrefs = dataModel.getPreferencesFromUser(userID2);
    int xLength = xPrefs.length();
    int yLength = yPrefs.length();

    if ((xLength == 0) || (yLength == 0)) {
      return Double.NaN;
    }

    long xIndex = xPrefs.getItemID(0);
    long yIndex = yPrefs.getItemID(0);
    int xPrefIndex = 0;
    int yPrefIndex = 0;

    double sumX = 0.0;
    double sumX2 = 0.0;
    double sumY = 0.0;
    double sumY2 = 0.0;
    double sumXY = 0.0;
    double sumXYdiff2 = 0.0;
    int count = 0;

    boolean hasInferrer = inferrer != null;
    boolean hasPrefTransform = prefTransform != null;

    while (true) {
      int compare = xIndex < yIndex ? -1 : xIndex > yIndex ? 1 : 0;
      if (hasInferrer || compare == 0) {
        double x;
        double y;
        if (xIndex == yIndex) {
          // Both users expressed a preference for the item
          if (hasPrefTransform) {
            x = prefTransform.getTransformedValue(xPrefs.get(xPrefIndex));
            y = prefTransform.getTransformedValue(yPrefs.get(yPrefIndex));
          } else {
            x = xPrefs.getValue(xPrefIndex);
            y = yPrefs.getValue(yPrefIndex);
          }
        } else {
          // Only one user expressed a preference, but infer the other one's preference and tally
          // as if the other user expressed that preference
          if (compare < 0) {
            // X has a value; infer Y's
            x =
                hasPrefTransform
                    ? prefTransform.getTransformedValue(xPrefs.get(xPrefIndex))
                    : xPrefs.getValue(xPrefIndex);
            y = inferrer.inferPreference(userID2, xIndex);
          } else {
            // compare > 0
            // Y has a value; infer X's
            x = inferrer.inferPreference(userID1, yIndex);
            y =
                hasPrefTransform
                    ? prefTransform.getTransformedValue(yPrefs.get(yPrefIndex))
                    : yPrefs.getValue(yPrefIndex);
          }
        }
        sumXY += x * y;
        sumX += x;
        sumX2 += x * x;
        sumY += y;
        sumY2 += y * y;
        double diff = x - y;
        sumXYdiff2 += diff * diff;
        count++;
      }
      if (compare <= 0) {
        if (++xPrefIndex >= xLength) {
          if (hasInferrer) {
            // Must count other Ys; pretend next X is far away
            if (yIndex == Long.MAX_VALUE) {
              // ... but stop if both are done!
              break;
            }
            xIndex = Long.MAX_VALUE;
          } else {
            break;
          }
        } else {
          xIndex = xPrefs.getItemID(xPrefIndex);
        }
      }
      if (compare >= 0) {
        if (++yPrefIndex >= yLength) {
          if (hasInferrer) {
            // Must count other Xs; pretend next Y is far away
            if (xIndex == Long.MAX_VALUE) {
              // ... but stop if both are done!
              break;
            }
            yIndex = Long.MAX_VALUE;
          } else {
            break;
          }
        } else {
          yIndex = yPrefs.getItemID(yPrefIndex);
        }
      }
    }

    // "Center" the data. If my math is correct, this'll do it.
    double result;
    if (centerData) {
      double n = count;
      double meanX = sumX / n;
      double meanY = sumY / n;
      // double centeredSumXY = sumXY - meanY * sumX - meanX * sumY + n * meanX * meanY;
      double centeredSumXY = sumXY - meanY * sumX;
      // double centeredSumX2 = sumX2 - 2.0 * meanX * sumX + n * meanX * meanX;
      double centeredSumX2 = sumX2 - meanX * sumX;
      // double centeredSumY2 = sumY2 - 2.0 * meanY * sumY + n * meanY * meanY;
      double centeredSumY2 = sumY2 - meanY * sumY;
      result = computeResult(count, centeredSumXY, centeredSumX2, centeredSumY2, sumXYdiff2);
    } else {
      result = computeResult(count, sumXY, sumX2, sumY2, sumXYdiff2);
    }

    if (similarityTransform != null) {
      result = similarityTransform.transformSimilarity(userID1, userID2, result);
    }

    if (!Double.isNaN(result)) {
      result = normalizeWeightResult(result, count, cachedNumItems);
    }
    return result;
  }
  @Override
  public final double itemSimilarity(long itemID1, long itemID2) throws TasteException {
    PreferenceArray xPrefs = dataModel.getPreferencesForItem(itemID1);
    PreferenceArray yPrefs = dataModel.getPreferencesForItem(itemID2);
    int xLength = xPrefs.length();
    int yLength = yPrefs.length();

    if ((xLength == 0) || (yLength == 0)) {
      return Double.NaN;
    }

    long xIndex = xPrefs.getUserID(0);
    long yIndex = yPrefs.getUserID(0);
    int xPrefIndex = 0;
    int yPrefIndex = 0;

    double sumX = 0.0;
    double sumX2 = 0.0;
    double sumY = 0.0;
    double sumY2 = 0.0;
    double sumXY = 0.0;
    double sumXYdiff2 = 0.0;
    int count = 0;

    // No, pref inferrers and transforms don't appy here. I think.

    while (true) {
      int compare = xIndex < yIndex ? -1 : xIndex > yIndex ? 1 : 0;
      if (compare == 0) {
        // Both users expressed a preference for the item
        double x = xPrefs.getValue(xPrefIndex);
        double y = yPrefs.getValue(yPrefIndex);
        sumXY += x * y;
        sumX += x;
        sumX2 += x * x;
        sumY += y;
        sumY2 += y * y;
        double diff = x - y;
        sumXYdiff2 += diff * diff;
        count++;
      }
      if (compare <= 0) {
        if (++xPrefIndex == xLength) {
          break;
        }
        xIndex = xPrefs.getUserID(xPrefIndex);
      }
      if (compare >= 0) {
        if (++yPrefIndex == yLength) {
          break;
        }
        yIndex = yPrefs.getUserID(yPrefIndex);
      }
    }

    double result;
    if (centerData) {
      // See comments above on these computations
      double n = (double) count;
      double meanX = sumX / n;
      double meanY = sumY / n;
      // double centeredSumXY = sumXY - meanY * sumX - meanX * sumY + n * meanX * meanY;
      double centeredSumXY = sumXY - meanY * sumX;
      // double centeredSumX2 = sumX2 - 2.0 * meanX * sumX + n * meanX * meanX;
      double centeredSumX2 = sumX2 - meanX * sumX;
      // double centeredSumY2 = sumY2 - 2.0 * meanY * sumY + n * meanY * meanY;
      double centeredSumY2 = sumY2 - meanY * sumY;
      result = computeResult(count, centeredSumXY, centeredSumX2, centeredSumY2, sumXYdiff2);
    } else {
      result = computeResult(count, sumXY, sumX2, sumY2, sumXYdiff2);
    }

    if (similarityTransform != null) {
      result = similarityTransform.transformSimilarity(itemID1, itemID2, result);
    }

    if (!Double.isNaN(result)) {
      result = normalizeWeightResult(result, count, cachedNumUsers);
    }
    return result;
  }
  private void splitOneUsersPrefs(
      double trainingPercentage,
      FastByIDMap<PreferenceArray> trainingPrefs,
      FastByIDMap<PreferenceArray> testPrefs,
      long userID,
      DataModel dataModel)
      throws TasteException {
    List<Preference> oneUserTrainingPrefs = null;
    List<Preference> oneUserTestPrefs = null;
    PreferenceArray prefs = dataModel.getPreferencesFromUser(userID);
    int size = prefs.length();
    boolean isInstanceOfContextualUserPreferenceArray =
        prefs instanceof ContextualUserPreferenceArray;

    for (int i = 0; i < size; i++) {
      Preference newPref =
          isInstanceOfContextualUserPreferenceArray
              ? new ContextualPreference(
                  userID,
                  prefs.getItemID(i),
                  prefs.getValue(i),
                  ((ContextualUserPreferenceArray) prefs).getContextualPreferences(i))
              : new GenericPreference(userID, prefs.getItemID(i), prefs.getValue(i));

      if (this.idrescorer != null && this.idrescorer.isFiltered(newPref.getItemID())) { // adiciona
        // ratings
        // de
        // source
        // domain
        // sempre
        // em
        // training
        // set
        if (oneUserTrainingPrefs == null) {
          oneUserTrainingPrefs = Lists.newArrayListWithCapacity(3);
        }
        oneUserTrainingPrefs.add(newPref);
        totalOfTrainingRatingsFromSource++;
        continue;
      }

      if (this.contextualCriteria != null
          && isInstanceOfContextualUserPreferenceArray) { // adiciona
        // ratings
        // de outro
        // contexto
        // sempre em
        // training
        // set
        ContextualPreference contextualPref = (ContextualPreference) newPref;
        if (!this.contextualCriteria.containsAllContextualAttributes(
            contextualPref.getContextualPreferences())) {
          if (oneUserTrainingPrefs == null) {
            oneUserTrainingPrefs = Lists.newArrayListWithCapacity(3);
          }
          oneUserTrainingPrefs.add(newPref);
          totalOfTrainingRatingsFromTargetWithoutContext++;
          continue;
        }
      }

      // para ratings do target e do contexto, fazer proporcao definida
      if (random.nextDouble() < trainingPercentage) {
        if (oneUserTrainingPrefs == null) {
          oneUserTrainingPrefs = Lists.newArrayListWithCapacity(3);
        }
        oneUserTrainingPrefs.add(newPref);
        totalOfTrainingRatingsFromTargetWithContext++;
      } else {
        if (oneUserTestPrefs == null) {
          oneUserTestPrefs = Lists.newArrayListWithCapacity(3);
        }
        oneUserTestPrefs.add(newPref);
        totalOfTestRatings++;
      }

      // OLD training/test set
      /*
       * if (random.nextDouble() < trainingPercentage) { if
       * (oneUserTrainingPrefs == null) { oneUserTrainingPrefs =
       * Lists.newArrayListWithCapacity(3); }
       * oneUserTrainingPrefs.add(newPref); totalOfTrainingRatings++; }
       * else { if (oneUserTestPrefs == null) { oneUserTestPrefs =
       * Lists.newArrayListWithCapacity(3); } //testa somente com um tipo
       * de rating (rescorer e/ou context) if(this.idrescorer == null &&
       * this.contextualCriteria == null){ oneUserTestPrefs.add(newPref);
       * totalOfTestRatings++; }else{ if(this.idrescorer != null &&
       * !this.idrescorer.isFiltered(newPref.getItemID())){
       * if(this.contextualCriteria != null &&
       * isInstanceOfContextualUserPreferenceArray){ ContextualPreference
       * contextualPref = (ContextualPreference) newPref;
       * if(this.contextualCriteria
       * .containsAllContextualAttributes(contextualPref
       * .getContextualPreferences())){ oneUserTestPrefs.add(newPref);
       * totalOfTestRatings++; } }else{ oneUserTestPrefs.add(newPref);
       * totalOfTestRatings++; } }else if(this.idrescorer == null &&
       * this.contextualCriteria != null &&
       * isInstanceOfContextualUserPreferenceArray){ ContextualPreference
       * contextualPref = (ContextualPreference) newPref;
       * if(this.contextualCriteria
       * .containsAllContextualAttributes(contextualPref
       * .getContextualPreferences())){ oneUserTestPrefs.add(newPref);
       * totalOfTestRatings++; } } } }
       */
    }
    if (oneUserTrainingPrefs != null) {
      trainingPrefs.put(
          userID,
          isInstanceOfContextualUserPreferenceArray
              ? new ContextualUserPreferenceArray(oneUserTrainingPrefs)
              : new GenericUserPreferenceArray(oneUserTrainingPrefs));
      if (oneUserTestPrefs != null) {
        testPrefs.put(
            userID,
            isInstanceOfContextualUserPreferenceArray
                ? new ContextualUserPreferenceArray(oneUserTestPrefs)
                : new GenericUserPreferenceArray(oneUserTestPrefs));
      }
    }
  }