@Override
  public double userSimilarity(long userID1, long userID2) throws TasteException {

    FastIDSet prefs1 = dataModel.getItemIDsFromUser(userID1);
    FastIDSet prefs2 = dataModel.getItemIDsFromUser(userID2);

    int prefs1Size = prefs1.size();
    int prefs2Size = prefs2.size();
    int intersectionSize =
        prefs1Size < prefs2Size ? prefs2.intersectionSize(prefs1) : prefs1.intersectionSize(prefs2);
    if (intersectionSize == 0) {
      return Double.NaN;
    }

    // int numItems = dataModel.getNumItems();
    // int numItems = Math.max(prefs1Size, prefs2Size);

    // int numItems = prefs1Size;

    //    double distance1 =  (double) (prefs1Size - intersectionSize)/(double) 2;
    //    double distance2 =  (double) (prefs2Size - intersectionSize)/(double) 2;
    //    double distance = (distance1 + distance2);

    double distance = (double) (prefs1Size - intersectionSize);

    double similarity = 1.0 / (1.0 + distance);
    ////// System.out.println( prefs1Size  + ", " + prefs2Size + ", " + intersectionSize + ", " +
    // similarity);
    return similarity;
  }
 @Override
 public int getNumUsersWithPreferenceFor(long itemID1, long itemID2) {
   FastIDSet userIDs1 = preferenceForItems.get(itemID1);
   if (userIDs1 == null) {
     return 0;
   }
   FastIDSet userIDs2 = preferenceForItems.get(itemID2);
   if (userIDs2 == null) {
     return 0;
   }
   return userIDs1.size() < userIDs2.size()
       ? userIDs2.intersectionSize(userIDs1)
       : userIDs1.intersectionSize(userIDs2);
 }
  @Test
  public void testStrategy() throws TasteException {
    FastIDSet itemIDsFromUser123 = new FastIDSet();
    itemIDsFromUser123.add(1L);

    FastIDSet itemIDsFromUser456 = new FastIDSet();
    itemIDsFromUser456.add(1L);
    itemIDsFromUser456.add(2L);

    List<Preference> prefs = new ArrayList<Preference>();
    prefs.add(new GenericPreference(123L, 1L, 1.0f));
    prefs.add(new GenericPreference(456L, 1L, 1.0f));
    PreferenceArray preferencesForItem1 = new GenericItemPreferenceArray(prefs);

    DataModel dataModel = EasyMock.createMock(DataModel.class);
    EasyMock.expect(dataModel.getPreferencesForItem(1L)).andReturn(preferencesForItem1);
    EasyMock.expect(dataModel.getItemIDsFromUser(123L)).andReturn(itemIDsFromUser123);
    EasyMock.expect(dataModel.getItemIDsFromUser(456L)).andReturn(itemIDsFromUser456);

    PreferenceArray prefArrayOfUser123 =
        new GenericUserPreferenceArray(Arrays.asList(new GenericPreference(123L, 1L, 1.0f)));

    CandidateItemsStrategy strategy = new PreferredItemsNeighborhoodCandidateItemsStrategy();

    EasyMock.replay(dataModel);

    FastIDSet candidateItems = strategy.getCandidateItems(123L, prefArrayOfUser123, dataModel);
    assertEquals(1, candidateItems.size());
    assertTrue(candidateItems.contains(2L));

    EasyMock.verify(dataModel);
  }
  @Override
  public double userSimilarity(long userID1, long userID2) throws TasteException {

    FastIDSet prefs1 = dataModel.getItemIDsFromUser(userID1);
    FastIDSet prefs2 = dataModel.getItemIDsFromUser(userID2);

    int prefs1Size = prefs1.size();
    int prefs2Size = prefs2.size();
    int intersectionSize =
        prefs1Size < prefs2Size ? prefs2.intersectionSize(prefs1) : prefs1.intersectionSize(prefs2);
    if (intersectionSize == 0) {
      return Double.NaN;
    }
    int numItems = dataModel.getNumItems();
    double logLikelihood =
        twoLogLambda(
            intersectionSize, prefs1Size - intersectionSize, prefs2Size, numItems - prefs2Size);
    return 1.0 - 1.0 / (1.0 + logLikelihood);
  }
  @Override
  public double userSimilarity(long userID1, long userID2) throws TasteException {

    DataModel dataModel = getDataModel();

    FastIDSet xPrefs = dataModel.getItemIDsFromUser(userID1);
    FastIDSet yPrefs = dataModel.getItemIDsFromUser(userID2);

    int xPrefsSize = xPrefs.size();
    int yPrefsSize = yPrefs.size();
    if (xPrefsSize == 0 && yPrefsSize == 0) {
      return Double.NaN;
    }
    if (xPrefsSize == 0 || yPrefsSize == 0) {
      return 0.0;
    }

    double intersection = 0.0;
    double union = 0.0;

    for (LongPrimitiveIterator it_item = xPrefs.iterator(); it_item.hasNext(); ) {
      long itemID = (long) it_item.nextLong();
      double weight = (double) getDataModel().getNumUsers() / mItemPrefNum.get(itemID);
      if (yPrefs.contains(itemID)) {
        intersection += weight;
        union -= weight;
      }
      union += weight;
    }
    for (LongPrimitiveIterator it_item = yPrefs.iterator(); it_item.hasNext(); ) {
      long itemID = (long) it_item.nextLong();
      double weight = (double) getDataModel().getNumUsers() / mItemPrefNum.get(itemID);
      union += weight;
    }

    return Math.log(intersection) / Math.log(union);
  }
 @Override
 public PreferenceArray getPreferencesForItem(long itemID) throws NoSuchItemException {
   FastIDSet userIDs = preferenceForItems.get(itemID);
   if (userIDs == null) {
     throw new NoSuchItemException(itemID);
   }
   PreferenceArray prefArray = new BooleanItemPreferenceArray(userIDs.size());
   int i = 0;
   LongPrimitiveIterator it = userIDs.iterator();
   while (it.hasNext()) {
     prefArray.setUserID(i, it.nextLong());
     prefArray.setItemID(i, itemID);
     i++;
   }
   return prefArray;
 }
 @Override
 public int getNumUsersWithPreferenceFor(long itemID) {
   FastIDSet userIDs1 = preferenceForItems.get(itemID);
   return userIDs1 == null ? 0 : userIDs1.size();
 }