diff --git a/src/main/java/org/apache/commons/text/similarity/DamerauLevenshteinDistance.java b/src/main/java/org/apache/commons/text/similarity/DamerauLevenshteinDistance.java index b266c1caa9..68a55f5c8c 100644 --- a/src/main/java/org/apache/commons/text/similarity/DamerauLevenshteinDistance.java +++ b/src/main/java/org/apache/commons/text/similarity/DamerauLevenshteinDistance.java @@ -16,6 +16,8 @@ */ package org.apache.commons.text.similarity; +import java.util.Objects; + /** * An algorithm for measuring the difference between two character sequences using the * Damerau-Levenshtein Distance. @@ -32,7 +34,7 @@ public class DamerauLevenshteinDistance implements EditDistance { private static int calculateCost(final SimilarityInput left, final SimilarityInput right, final int leftIndex, final int rightIndex, final int[] curr, final int[] prev, final int[] prevPrev) { - final int cost = left.at(leftIndex - 1) == right.at(rightIndex - 1) ? 0 : 1; + final int cost = Objects.equals(left.at(leftIndex - 1), right.at(rightIndex - 1)) ? 0 : 1; // Select cheapest operation int value = Math.min( Math.min( @@ -44,8 +46,8 @@ private static int calculateCost(final SimilarityInput left, final Simila // Check if adjacent characters are the same -> transpose if cheaper if (leftIndex > 1 && rightIndex > 1 - && left.at(leftIndex - 1) == right.at(rightIndex - 2) - && left.at(leftIndex - 2) == right.at(rightIndex - 1)) { + && Objects.equals(left.at(leftIndex - 1), right.at(rightIndex - 2)) + && Objects.equals(left.at(leftIndex - 2), right.at(rightIndex - 1))) { // Use cost here, to properly handle two subsequent equal letters value = Math.min(value, prevPrev[rightIndex - 2] + cost); } diff --git a/src/test/java/org/apache/commons/text/similarity/DamerauLevenshteinDistanceTest.java b/src/test/java/org/apache/commons/text/similarity/DamerauLevenshteinDistanceTest.java index 65f664bb57..a532ba31c7 100644 --- a/src/test/java/org/apache/commons/text/similarity/DamerauLevenshteinDistanceTest.java +++ b/src/test/java/org/apache/commons/text/similarity/DamerauLevenshteinDistanceTest.java @@ -77,7 +77,14 @@ static Stream limitedDamerauLevenshteinDistanceTestCases() { Arguments.of("xyxyxyxyxy", "yxyxyxyxyx", 4, 2), Arguments.of("aaaaabbbbbccccc", "cccccbbbbbaaaaa", 5, -1), Arguments.of("thequickbrownfoxjumpsoverthelazydog", "thequickbrownfoxjumpsovrethelazydog", 1, 1), - Arguments.of("antidisestablishmentarianism", "antidisestablishmentarianisn", 3, 1) + Arguments.of("antidisestablishmentarianism", "antidisestablishmentarianisn", 3, 1), + // Non-ASCII characters are outside the Character.valueOf cache, so identical inputs must still measure zero. + Arguments.of("caf\u00e9", "caf\u00e9", 1, 0), + Arguments.of("\u4f60\u597d", "\u4f60\u597d", 1, 0), + Arguments.of("na\u00efve", "na\u00efve", 1, 0), + // Transposing two adjacent non-ASCII characters costs one edit, exactly as it does for ASCII. + Arguments.of("caf\u00e9\u00e8", "caf\u00e8\u00e9", 1, 1), + Arguments.of("\u4f60\u597d", "\u597d\u4f60", 1, 1) ); } @@ -123,7 +130,16 @@ static Stream unlimitedDamerauLevenshteinDistanceTestCases() { Arguments.of("xyxyxyxyxy", "yxyxyxyxyx", 2), Arguments.of("aaaaabbbbbccccc", "cccccbbbbbaaaaa", 10), Arguments.of("thequickbrownfoxjumpsoverthelazydog", "thequickbrownfoxjumpsovrethelazydog", 1), - Arguments.of("antidisestablishmentarianism", "antidisestablishmentarianisn", 1) + Arguments.of("antidisestablishmentarianism", "antidisestablishmentarianisn", 1), + // Non-ASCII characters are outside the Character.valueOf cache, so identical inputs must still measure zero. + Arguments.of("caf\u00e9", "caf\u00e9", 0), + Arguments.of("\u4f60\u597d", "\u4f60\u597d", 0), + Arguments.of("na\u00efve", "na\u00efve", 0), + Arguments.of("\ud83d\ude00", "\ud83d\ude00", 0), + // Transposing two adjacent non-ASCII characters costs one edit, exactly as it does for ASCII. + Arguments.of("caf\u00e9\u00e8", "caf\u00e8\u00e9", 1), + Arguments.of("\u4f60\u597d", "\u597d\u4f60", 1), + Arguments.of("\u00e9x", "x\u00e9", 1) ); }