diff --git a/FuzzySearch.Test/FuzzyTests.cs b/FuzzySearch.Test/FuzzyTests.cs index ae0296b..97f6511 100644 --- a/FuzzySearch.Test/FuzzyTests.cs +++ b/FuzzySearch.Test/FuzzyTests.cs @@ -218,6 +218,22 @@ public void Contains_WithScore_CamelCaseMatch_GetsBonus() Assert.IsGreaterThanOrEqualTo(Fuzzy.camelCaseMatchBonus, score, "Score should include camelCase bonus"); } + [TestMethod] + public void Contains_WithScore_LatePrefixMatch_ScoresLowerThanStartMatch() + { + // Arrange + string pattern = "foo"; + + // Act + bool earlyResult = Fuzzy.Contains("foo", pattern, out int earlyScore); + bool lateResult = Fuzzy.Contains("xxxxxxxxxxfoo", pattern, out int lateScore); + + // Assert + Assert.IsTrue(earlyResult, "Start-position match should return true."); + Assert.IsTrue(lateResult, "Late-position match should return true."); + Assert.IsGreaterThan(lateScore, earlyScore, "Match at the start should score higher than a late prefix match."); + } + #endregion #region Apply Bonuses Tests @@ -329,7 +345,23 @@ public void PenalizeNonPatternCharacters_FirstPatternChar_AppliesPrefixPenalty() int initialScore = 10; int patternIdx = 0; int strIdx = 3; // 3 chars before first match - int expectedPenalty = Math.Max(strIdx * Fuzzy.unmatchedPrefixLetterPenalty, Fuzzy.maxPrefixPenalty); + int expectedPenalty = -3; + + // Act + int result = Fuzzy.PenalizeNonPatternCharacters(initialScore, patternIdx, strIdx); + + // Assert + Assert.AreEqual(initialScore + expectedPenalty, result); + } + + [TestMethod] + public void PenalizeNonPatternCharacters_FirstPatternChar_CapsPrefixPenalty() + { + // Arrange + int initialScore = 10; + int patternIdx = 0; + int strIdx = 10; + int expectedPenalty = -5; // Act int result = Fuzzy.PenalizeNonPatternCharacters(initialScore, patternIdx, strIdx); diff --git a/FuzzySearch/Fuzzy.cs b/FuzzySearch/Fuzzy.cs index 7408e4c..6cfd9ea 100644 --- a/FuzzySearch/Fuzzy.cs +++ b/FuzzySearch/Fuzzy.cs @@ -27,10 +27,10 @@ public static class Fuzzy internal const int camelCaseMatchBonus = 10; /// The penalty for each unmatched character at the beginning of the string. - internal const int unmatchedPrefixLetterPenalty = 0; + internal const int unmatchedPrefixLetterPenalty = -1; /// The maximum prefix penalty that can be applied. - internal const int maxPrefixPenalty = 0; + internal const int maxPrefixPenalty = -5; /// The penalty for each unmatched character in the string. internal const int unmatchedLetterPenalty = -1; @@ -119,8 +119,6 @@ internal static int CalculateScore(ReadOnlySpan subject, ReadOnlySpan matchedIndices = []; - // Loop over characters in subject while (strIdx != strLength) { @@ -139,7 +137,6 @@ internal static int CalculateScore(ReadOnlySpan subject, ReadOnlySpan subject, ReadOnlySpan