public List<SemanticPath> getSemanticPaths(ConceptVector cv, int conceptLimit, int topPaths)
     throws WikitException {
   if (cv == null || cv.size() == 0) {
     return new ArrayList<>();
   }
   SortedMap<Integer, Double> categoryDistribution = getCategoryDistribution(cv, conceptLimit);
   return getSemanticPaths(constructCategoryTree(categoryDistribution), topPaths);
 }
  /**
   * Given concept vector which represents a text, calculate the category probabilities.
   *
   * @param cv
   * @param conceptLimit only conceptLimit concepts are used to calculate the category path
   *     distribution
   * @return category id -> scores
   */
  public SortedMap<Integer, Double> getCategoryDistribution(ConceptVector cv, int conceptLimit)
      throws WikitException {
    ConceptIterator conceptIterator = cv.orderedIterator();

    // Category ID --> Category
    Map<Integer, Category> bags = new HashMap<>();
    int count = 0;
    while (conceptIterator.next() && count++ < conceptLimit) {
      int conceptId = conceptIterator.getId();
      ConceptItem conceptItem = new ConceptItem(conceptId, conceptIterator.getValue());
      Set<Integer> catIds = treeCache.getCategoryIdsByConceptId(conceptId);
      conceptItem.catIds = catIds;

      for (int catId : catIds) {
        if (bags.containsKey(catId)) {
          bags.get(catId).addItem(conceptItem);
        } else {
          Category category = new Category(catId);
          category.addItem(conceptItem);
          bags.put(catId, category);
        }
      }
    }
    double totalScore = 0;
    SortedMap<Integer, Double> sortedScores = null;
    // category id -> score
    Map<Integer, Double> scores = new HashMap<>();

    //        LOG.info("Method 1:");
    //        for (Map.Entry<Integer, Category> entry : bags.entrySet()) {
    //            double categoryScore = 0;
    //            Category category = entry.getValue();
    //            for (ConceptItem item : category.concepts()) {
    //                categoryScore += item.value;
    //            }
    //            double normalizedScore = categoryScore/entry.getValue().size();
    //            scores.put(entry.getKey(), normalizedScore);
    //            totalScore += normalizedScore;
    //        }
    //
    //        sortedScores = new TreeMap<Integer,Double>(new ValueComparator(scores));
    //        sortedScores.putAll(scores);

    // Method 2, take link into account"
    double LAMBDA = 0.6;
    totalScore = 0;
    scores = new HashMap<>();
    for (Map.Entry<Integer, Category> entry : bags.entrySet()) {
      double categoryScore = 0;
      Category category = entry.getValue();
      for (ConceptItem conceptItem : category.concepts()) {
        double v1 = conceptItem.value;
        double v2 = 0.0;
        Set<Integer> inlinkIds = conceptCache.getInlinkIds(conceptItem.id);
        for (int inlinkId : inlinkIds) {
          if (category.hasConcept(inlinkId)) {
            v2 += category.getConcept(inlinkId).value;
            // System.out.println(inlink + "==>" + item.id + "\t" + item.title);
          }
        }
        if (inlinkIds.size() > 0) {
          v2 = v2 / inlinkIds.size(); // normalize
        }

        // if item connected with
        double v = LAMBDA * v1 + (1 - LAMBDA) * v2;
        categoryScore += v;
      }

      double normalizedScore = categoryScore / category.size();
      scores.put(category.id, normalizedScore);
      totalScore += normalizedScore;
    }

    sortedScores = new TreeMap<Integer, Double>(new ValueComparator<>(scores));

    boolean normalize = true;
    if (normalize) {
      // normalized the value
      for (Map.Entry<Integer, Double> entry : scores.entrySet()) {
        sortedScores.put(entry.getKey(), entry.getValue() / totalScore);
      }
    } else {
      sortedScores.putAll(scores);
    }
    return sortedScores;
  }