/*
 * LensKit, an open source recommender systems toolkit.
 * Copyright 2010-2012 Regents of the University of Minnesota and contributors
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package org.grouplens.lenskit.slopeone;

import it.unimi.dsi.fastutil.longs.LongIterator;

import javax.inject.Inject;
import javax.inject.Provider;

import org.grouplens.grapht.annotation.Transient;
import org.grouplens.lenskit.baseline.BaselinePredictor;
import org.grouplens.lenskit.collections.LongSortedArraySet;
import org.grouplens.lenskit.data.pref.PreferenceDomain;
import org.grouplens.lenskit.data.snapshot.PreferenceSnapshot;
import org.grouplens.lenskit.params.Damping;
import org.grouplens.lenskit.params.NormalizedSnapshot;
import org.grouplens.lenskit.vectors.SparseVector;

/**
 * Pre-computes the deviations and number of mutual rating users for every pair
 *  of items and stores the results in a <tt>DeviationMatrix</tt> and
 *  <tt>CoratingMatrix</tt>. These matrices are later used by a
 *  <tt>SlopeOneRatingPredictor</tt>.
 */
public class SlopeOneModelProvider implements Provider<SlopeOneModel> {
    private final SlopeOneModelDataAccumulator accumulator;
    
    private final BaselinePredictor baseline;
    private final PreferenceDomain domain;
    
    private final PreferenceSnapshot snapshot;
    
    @Inject
    public SlopeOneModelProvider(@Transient PreferenceSnapshot snapshot,
                                // FIXME: can this be nullable? Why have two snapshots?
                                @Transient @NormalizedSnapshot PreferenceSnapshot normalized,
                                BaselinePredictor predictor,
                                PreferenceDomain dom,
                                @Damping double damping) {
        if (normalized != null) {
            this.snapshot = normalized;
        } else {
            this.snapshot = snapshot;
        }
        
        domain = dom;
        baseline = predictor;
        accumulator = new SlopeOneModelDataAccumulator(damping, this.snapshot);
    }

    /**
     * Constructs a {@link SlopeOneModel} and the necessary matrices from
     * a {@link org.grouplens.lenskit.data.snapshot.PreferenceSnapshot}.
     */
    @Override
    public SlopeOneModel get() {
        for (long currentUser : snapshot.getUserIds()) {
            SparseVector ratings = snapshot.userRatingVector(currentUser);
            LongIterator iter = ratings.keySet().iterator();
            while (iter.hasNext()) {
                long item1 = iter.next();
                LongIterator iter2 = ratings.keySet().tailSet(item1).iterator();
                if (iter2.hasNext()) iter2.next();
                while (iter2.hasNext()) {
                    long item2 = iter2.next();
                    accumulator.putRatingPair(item1, ratings.get(item1), item2, ratings.get(item2));
                }
            }
        }
        LongSortedArraySet items = new LongSortedArraySet(snapshot.getItemIds());
        return new SlopeOneModel(accumulator.buildCoratingMatrix(), accumulator.buildDeviationMatrix(), 
                                 baseline, items, domain);
    }
}

@@ -958,68 +958,8 @@
 or;%0A
-%0Aimport javax.inject.Inject;%0Aimport javax.inject.Provider;%0A%0A
 impo
@@ -1122,16 +1122,70 @@
 raySet;%0A
+import org.grouplens.lenskit.data.history.UserVector;%0A
 import o
@@ -1288,32 +1288,144 @@
 erenceSnapshot;%0A
+import org.grouplens.lenskit.norm.IdentityVectorNormalizer;%0Aimport org.grouplens.lenskit.norm.VectorNormalizer;%0A
 import org.group
@@ -1489,34 +1489,36 @@
 .params.
-NormalizedSnapshot
+UserVectorNormalizer
 ;%0Aimport
@@ -1563,16 +1563,75 @@
 ector;%0A%0A
+import javax.inject.Inject;%0Aimport javax.inject.Provider;%0A%0A
 /**%0A * P
@@ -2160,24 +2160,81 @@
 apshot;%0A    
+private VectorNormalizer%3C? super UserVector%3E normalizer;%0A
 %0A    @Inject
@@ -2301,20 +2301,16 @@
 hot snap
-shot
 ,%0A      
@@ -2339,158 +2339,86 @@
     
-// FIXME: can this be nullable? Why have two snapshots?%0A                                @Transient @NormalizedSnapshot PreferenceSnapshot normalized
+ @Transient @UserVectorNormalizer VectorNormalizer%3C? super UserVector%3E norm
 ,%0A
+ 
     
@@ -2506,16 +2506,17 @@
         
+ 
 Preferen
@@ -2529,16 +2529,17 @@
 in dom,%0A
+ 
         
@@ -2601,71 +2601,105 @@
     
-if (normalized != null) %7B%0A            this.snapshot = n
+snapshot = snap;%0A        if (norm == null) %7B%0A            normalizer = new IdentityVectorN
 ormalize
 d;%0A 
@@ -2694,17 +2694,19 @@
 ormalize
-d
+r()
 ;%0A      
@@ -2732,32 +2732,25 @@
     
-this.snapshot = snapshot
+normalizer = norm
 ;%0A  
@@ -2757,24 +2757,16 @@
       %7D%0A
-        
 %0A       
@@ -3176,22 +3176,20 @@
         
-Spar
+U
 se
+r
 Vector r
@@ -3237,16 +3237,87 @@
 tUser);%0A
+            SparseVector normed = normalizer.normalize(ratings, null);%0A
         
@@ -3340,23 +3340,22 @@
  iter = 
-ratings
+normed
 .keySet(
@@ -3484,23 +3484,22 @@
 iter2 = 
-ratings
+normed
 .keySet(
@@ -3720,23 +3720,22 @@
 (item1, 
-ratings
+normed
 .get(ite
@@ -3746,23 +3746,22 @@
  item2, 
-ratings
+normed
 .get(ite

Use the user vector normalizer directly in Slope-One