/*
 * LensKit, an open source recommender systems toolkit.
 * Copyright 2010-2014 LensKit Contributors.  See CONTRIBUTORS.md.
 * Work on LensKit has been funded by the National Science Foundation under
 * grants IIS 05-34939, 08-08692, 08-12148, and 10-17697.
 *
 * 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.lenskit.knn.item;

import it.unimi.dsi.fastutil.longs.Long2DoubleMap;
import org.grouplens.lenskit.transform.normalize.VectorTransformation;
import org.grouplens.lenskit.vectors.MutableSparseVector;

import java.util.List;

/**
 * Score accumulator for item-item recommendation.  It is used to record the results of the item-item CF
 * scoring of one or more items.
 *
 * Accumulating results here allows us to avoid memory allocations when detailed results are not required.
 */
public abstract class ItemItemScoreAccumulator {
    private ItemItemScoreAccumulator() {}

    /**
     * Add a score to the accumulator.
     * @param item The item to score.
     * @param score The score.
     * @param nnbrs The number of neighbors used.
     * @param weight The total neighbor weight.
     */
    public abstract void add(long item, double score, int nnbrs, double weight);

    /**
     * Apply the reverse of a transform to the results.
     * @param transform The transform to apply.
     */
    public abstract void applyReversedTransform(VectorTransformation transform);

    /**
     * Construct an accumulator that will store items and scores in a map.
     * @param receiver The map to receive the results.
     * @return The accumulator.
     */
    static ItemItemScoreAccumulator basic(Long2DoubleMap receiver) {
        return new BasicAccumulator(receiver);
    }

    /**
     * Construct an accumulator that will store full detailed results in a map.
     * @param receiver The map to receive the results.
     * @return The accumulator.
     */
    static ItemItemScoreAccumulator detailed(List<ItemItemResult> receiver) {
        return new DetailedAccumulator(receiver);
    }

    private static class BasicAccumulator extends ItemItemScoreAccumulator {
        private final Long2DoubleMap receiver;

        BasicAccumulator(Long2DoubleMap recv) {
            receiver = recv;
        }

        @Override
        public void add(long item, double score, int nnbrs, double weight) {
            receiver.put(item, score);
        }

        @Override
        public void applyReversedTransform(VectorTransformation transform) {
            MutableSparseVector vec = MutableSparseVector.create(receiver);
            transform.unapply(vec);
            receiver.putAll(vec.asMap());
        }
    }

    private static class DetailedAccumulator extends ItemItemScoreAccumulator {
        private final List<ItemItemResult> receiver;

        DetailedAccumulator(List<ItemItemResult> recv) {
            receiver = recv;
        }

        @Override
        public void add(long item, double score, int nnbrs, double weight) {
            receiver.add(new ItemItemResult(item, score, nnbrs, weight));
        }

        @Override
        public void applyReversedTransform(VectorTransformation transform) {
            int n = receiver.size();
            for (int i = 0; i < n; i++) {
                ItemItemResult res = receiver.get(i);
                receiver.set(i, res.rescore(transform.unapply(res.getId(), res.getScore())));
            }
        }
    }
}

@@ -1068,16 +1068,112 @@
 bleMap;%0A
+import it.unimi.dsi.fastutil.longs.LongOpenHashSet;%0Aimport it.unimi.dsi.fastutil.longs.LongSet;%0A
 import o
@@ -3487,32 +3487,95 @@
 reAccumulator %7B%0A
+        private final LongSet itemIds = new LongOpenHashSet();%0A
         private 
@@ -3690,32 +3690,133 @@
 eceiver = recv;%0A
+            for (ItemItemResult res: recv) %7B%0A                itemIds.add(res.getId());%0A            %7D%0A
         %7D%0A%0A     
@@ -3975,24 +3975,55 @@
 , weight));%0A
+            itemIds.add(item);%0A
         %7D%0A%0A 
@@ -4108,32 +4108,260 @@
 on transform) %7B%0A
+            MutableSparseVector vec = MutableSparseVector.create(itemIds);%0A            for (ItemItemResult res: receiver) %7B%0A                vec.set(res.getId(), res.getScore());%0A            %7D%0A            transform.unapply(vec);%0A
             int 
@@ -4525,51 +4525,25 @@
 ore(
-transform.unapply(res.getId(), 
+vec.get(
 res.get
-Score
+Id
 ()))

Improve item-item score performance