package com.db.service.main;

import com.db.service.ITrainItemService;
import com.db.service.ITrainUserService;
import com.db.service.model.TrainUser;
import com.elab.core.utils.ObjectUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author liuhx
 * @create 2019/04/02 16:22
 * @email liuhx@elab-plus.com
 **/
@RunWith(SpringJUnit4ClassRunner.class)  //使用junit4进行测试
@ContextConfiguration
        ({"classpath:applicationContext-*.xml",})
public class HxCase {
    @Autowired
    private ITrainUserService trainUserService;
    @Autowired
    private ITrainItemService trainItemService;

    public List<TrainUser> findByUserList() throws Exception {
        TrainUser user = new TrainUser();
        List<TrainUser> list = trainUserService.selectByList(user);
        return list;
    }

    @Test
    public void getUserItemSize() throws Exception {
        List<TrainUser> resultList = findByUserList();
        //建立物品到用户的倒排表 eg: a A B
        Map<Integer, List<TrainUser>> itemList = resultList.stream()
                .filter(trainUser -> ObjectUtils.isNotEmpty(trainUser.getItemId()))
                .collect(Collectors.groupingBy(TrainUser::getItemId));

        Map<Integer, List<TrainUser>> userList = resultList.stream()
                .filter(trainUser -> ObjectUtils.isNotEmpty(trainUser.getUserId()))
                .collect(Collectors.groupingBy(TrainUser::getUserId));


        System.out.println(itemList.size());
        int userSize = userList.size();
        /*建立用户稀疏矩阵，用于用户相似度计算【相似度矩阵】*/
        int[][] sparseMatrix = new int[userSize][userSize];

        Map<Integer, Integer> userItemLength = new HashMap<>();//存储每一个用户ID对应的不同物品总数
        Map<Integer, Integer> userID = new HashMap<>();//辅助存储每一个用户的用户ID映射
        Map<Integer, Integer> idUser = new HashMap<>();//辅助存储每一个ID对应的用户映射
        Set<Integer> items = new HashSet<>();//辅助存储物品集合，这里对应存的是物品的id

        AtomicInteger count = new AtomicInteger();
        userList.entrySet().iterator().forEachRemaining(user->{
            userItemLength.put(user.getKey(), user.getValue().size());
            userID.put(user.getKey(), count.intValue());//用户ID与稀疏矩阵建立对应关系
            idUser.put(count.intValue(), user.getKey());
            count.addAndGet(1);
        });
        itemList.entrySet().iterator().forEachRemaining(item->{
            items.add(item.getKey());
        });

        //计算相似度矩阵【稀疏】
        Set<Map.Entry<Integer, List<TrainUser>>> entrySet = itemList.entrySet();
        Iterator<Map.Entry<Integer, List<TrainUser>>> iterator = entrySet.iterator();
        while(iterator.hasNext()) {
            List<TrainUser> commonUsers = iterator.next().getValue();
            for (TrainUser user_u : commonUsers) {
                for (TrainUser user_v : commonUsers) {
                    if(user_u.getUserId().equals(user_v.getUserId())){
                        continue;
                    }
                    sparseMatrix[userID.get(user_u.getUserId())][userID.get(user_v.getUserId())] += 1;//计算用户u与用户v都有正反馈的物品总数
                }
            }
        }

        int recommendUser = 1;
        //计算用户之间的相似度【余弦相似性】
        int recommendUserId = userID.get(recommendUser);
        for (int j = 0;j < sparseMatrix.length; j++) {
            if(j != recommendUserId){
                System.out.println(idUser.get(recommendUserId)+"--"+idUser.get(j)+"相似度:"+sparseMatrix[recommendUserId][j]/Math.sqrt(userItemLength.get(idUser.get(recommendUserId))*userItemLength.get(idUser.get(j))));
            }
        }
        //计算指定用户recommendUser的物品推荐度
        for(Integer item: items){//遍历每一件物品
            List<TrainUser> users = itemList.get(item);//得到购买当前物品的所有用户集合
            List<Integer> integers = users.stream().filter(trainUser -> ObjectUtils.isNotEmpty(trainUser.getUserId())).map(TrainUser::getUserId).collect(Collectors.toList());
            if(!integers.contains(recommendUser)){//如果被推荐用户没有购买当前物品，则进行推荐度计算
                double itemRecommendDegree = 0.0;
                for(TrainUser user: users){
                    itemRecommendDegree += sparseMatrix[userID.get(recommendUser)][userID.get(user.getUserId())]/Math.sqrt(userItemLength.get(recommendUser)*userItemLength.get(user.getUserId()));//推荐度计算
                }
                System.out.println("The item "+item+" for "+recommendUser +"'s recommended degree:"+itemRecommendDegree);
            }
        }
    }
}
