public double getNormalizedEditDistance(String source, String target) {

    ImmutableList<String> sourceTerms = NamingConventions.splitToLowercaseTerms(source);
    ImmutableList<String> targetTerms = NamingConventions.splitToLowercaseTerms(target);

    // costMatrix[s][t] is the edit distance between source term s and target term t
    double[][] costMatrix =
        sourceTerms.stream()
            .map(s -> targetTerms.stream().mapToDouble(t -> editDistanceFn.apply(s, t)).toArray())
            .toArray(double[][]::new);

    // worstCaseMatrix[s][t] is the worst case distance between source term s and target term t
    double[][] worstCaseMatrix =
        sourceTerms.stream()
            .map(s -> s.length())
            .map(
                s ->
                    targetTerms.stream()
                        .map(t -> t.length())
                        .mapToDouble(t -> maxDistanceFn.apply(s, t))
                        .toArray())
            .toArray(double[][]::new);

    double[] sourceTermDeletionCosts =
        sourceTerms.stream().mapToDouble(s -> maxDistanceFn.apply(s.length(), 0)).toArray();

    double[] targetTermAdditionCosts =
        targetTerms.stream().mapToDouble(s -> maxDistanceFn.apply(0, s.length())).toArray();

    // this is an array of assignments of source terms to target terms. If assignments[i] contains
    // the value j this means that source term i has been assigned to target term j
    // There will be one entry in cost for each source term:
    // - If there are more source terms than target terms then some will be unassigned - value -1
    // - If there are a fewer source terms than target terms then some target terms will not be
    //    referenced in the array
    int[] assignments = new HungarianAlgorithm(costMatrix).execute();
    double assignmentCost =
        computeCost(assignments, costMatrix, sourceTermDeletionCosts, targetTermAdditionCosts);

    double maxCost =
        computeCost(assignments, worstCaseMatrix, sourceTermDeletionCosts, targetTermAdditionCosts);

    return assignmentCost / maxCost;
  }
@Test
  public void getNormalizedEditDistance_returnsMatch_withPermutedTerms() {
    TermEditDistance termEditDistance = new TermEditDistance();
    String sourceIdentifier = "fooBarBaz";
    String targetIdentifier = "bazFooBar";

    double distance =
        termEditDistance.getNormalizedEditDistance(sourceIdentifier, targetIdentifier);

    assertThat(distance).isEqualTo(0.0);
  }