/**
   * Traverses valid outward links from {@code src} node. If {@code src} equals {@code dest}, then a
   * score is computed. Of all possible paths from this state, the maximum score is returned. From
   * this state, only side directions are valid.
   */
  private static double hsoState5(int depth, int dirChange, Synset src, Synset dest) {
    double bestScore = (src.equals(dest)) ? C - depth - dirChange : 0;
    depth++;

    if (depth > 5) return Math.max(bestScore, 0);

    for (Relation relation : SIDE_RELATION)
      for (Synset related : src.getRelations(relation))
        bestScore = Math.max(bestScore, hsoState5(depth, dirChange, related, dest));

    return bestScore;
  }
  /** {@inheritDoc} */
  public double similarity(Synset synset1, Synset synset2) {
    double bestScore = 0;
    for (Relation relation : UP_RELATION)
      for (Synset related : synset1.getRelations(relation))
        bestScore = Math.max(bestScore, hsoState1(1, 0, related, synset2));

    for (Relation relation : DOWN_RELATION)
      for (Synset related : synset1.getRelations(relation))
        bestScore = Math.max(bestScore, hsoState3(1, 0, related, synset2));

    for (Relation relation : SIDE_RELATION)
      for (Synset related : synset1.getRelations(relation))
        bestScore = Math.max(bestScore, hsoState2(1, 0, related, synset2));

    // With k == 1 the minimum score possible is 0 and the maximum
    // score is C.  Divide by C so that it's within a range of 0 to 1.
    return bestScore / C;
  }