2323 */
2424
2525using System ;
26+ using System . Data . SqlTypes ;
2627using System . Linq ;
2728using F23 . StringSimilarity . Interfaces ;
2829// ReSharper disable SuggestVarOrType_Elsewhere
@@ -38,7 +39,7 @@ namespace F23.StringSimilarity
3839 /// Jaro-Winkler was developed in the area of record linkage (duplicate
3940 /// detection) (Winkler, 1990). It returns a value in the interval [0.0, 1.0].
4041 /// The distance is computed as 1 - Jaro-Winkler similarity.
41- public class JaroWinkler : INormalizedStringSimilarity , INormalizedStringDistance
42+ public class JaroWinkler : INormalizedStringSimilarity , INormalizedStringDistance , INormalizedSpanSimilarity , INormalizedSpanDistance
4243 {
4344 private const double DEFAULT_THRESHOLD = 0.7 ;
4445 private const int THREE = 3 ;
@@ -75,6 +76,10 @@ public JaroWinkler(double threshold)
7576 /// <returns>The Jaro-Winkler similarity in the range [0, 1]</returns>
7677 /// <exception cref="ArgumentNullException">If s1 or s2 is null.</exception>
7778 public double Similarity ( string s1 , string s2 )
79+ => Similarity ( s1 . AsSpan ( ) , s2 . AsSpan ( ) ) ;
80+
81+ public double Similarity < T > ( ReadOnlySpan < T > s1 , ReadOnlySpan < T > s2 )
82+ where T : IEquatable < T >
7883 {
7984 if ( s1 == null )
8085 {
@@ -86,7 +91,7 @@ public double Similarity(string s1, string s2)
8691 throw new ArgumentNullException ( nameof ( s2 ) ) ;
8792 }
8893
89- if ( s1 . Equals ( s2 ) )
94+ if ( s1 . SequenceEqual ( s2 ) )
9095 {
9196 return 1f ;
9297 }
@@ -117,10 +122,15 @@ public double Similarity(string s1, string s2)
117122 /// <exception cref="ArgumentNullException">If s1 or s2 is null.</exception>
118123 public double Distance ( string s1 , string s2 )
119124 => 1.0 - Similarity ( s1 , s2 ) ;
125+
126+ public double Distance < T > ( ReadOnlySpan < T > s1 , ReadOnlySpan < T > s2 )
127+ where T : IEquatable < T >
128+ => 1.0 - Similarity ( s1 , s2 ) ;
120129
121- private static int [ ] Matches ( string s1 , string s2 )
130+ private static int [ ] Matches < T > ( ReadOnlySpan < T > s1 , ReadOnlySpan < T > s2 )
131+ where T : IEquatable < T >
122132 {
123- string max , min ;
133+ ReadOnlySpan < T > max , min ;
124134 if ( s1 . Length > s2 . Length )
125135 {
126136 max = s1 ;
@@ -141,11 +151,11 @@ private static int[] Matches(string s1, string s2)
141151 int matches = 0 ;
142152 for ( int mi = 0 ; mi < min . Length ; mi ++ )
143153 {
144- char c1 = min [ mi ] ;
154+ var c1 = min [ mi ] ;
145155 for ( int xi = Math . Max ( mi - range , 0 ) ,
146156 xn = Math . Min ( mi + range + 1 , max . Length ) ; xi < xn ; xi ++ )
147157 {
148- if ( ! match_flags [ xi ] && c1 == max [ xi ] )
158+ if ( ! match_flags [ xi ] && c1 . Equals ( max [ xi ] ) )
149159 {
150160 match_indexes [ mi ] = xi ;
151161 match_flags [ xi ] = true ;
@@ -154,8 +164,8 @@ private static int[] Matches(string s1, string s2)
154164 }
155165 }
156166 }
157- char [ ] ms1 = new char [ matches ] ;
158- char [ ] ms2 = new char [ matches ] ;
167+ T [ ] ms1 = new T [ matches ] ;
168+ T [ ] ms2 = new T [ matches ] ;
159169 for ( int i = 0 , si = 0 ; i < min . Length ; i ++ )
160170 {
161171 if ( match_indexes [ i ] != - 1 )
@@ -175,15 +185,15 @@ private static int[] Matches(string s1, string s2)
175185 int transpositions = 0 ;
176186 for ( int mi = 0 ; mi < ms1 . Length ; mi ++ )
177187 {
178- if ( ms1 [ mi ] != ms2 [ mi ] )
188+ if ( ! ms1 [ mi ] . Equals ( ms2 [ mi ] ) )
179189 {
180190 transpositions ++ ;
181191 }
182192 }
183193 int prefix = 0 ;
184194 for ( int mi = 0 ; mi < min . Length ; mi ++ )
185195 {
186- if ( s1 [ mi ] == s2 [ mi ] )
196+ if ( s1 [ mi ] . Equals ( s2 [ mi ] ) )
187197 {
188198 prefix ++ ;
189199 }
0 commit comments