Sorry, I'm pretty *unconversant in git. Now, it should be ok.*

----
With best regards,
Alexander Korotkov.
*** a/contrib/fuzzystrmatch/fuzzystrmatch.c
--- b/contrib/fuzzystrmatch/fuzzystrmatch.c
***************
*** 61,66 **** PG_MODULE_MAGIC;
--- 61,68 ----
   */
  extern Datum levenshtein_with_costs(PG_FUNCTION_ARGS);
  extern Datum levenshtein(PG_FUNCTION_ARGS);
+ extern Datum levenshtein_less_equal_with_costs(PG_FUNCTION_ARGS);
+ extern Datum levenshtein_less_equal(PG_FUNCTION_ARGS);
  extern Datum metaphone(PG_FUNCTION_ARGS);
  extern Datum soundex(PG_FUNCTION_ARGS);
  extern Datum difference(PG_FUNCTION_ARGS);
***************
*** 92,98 **** soundex_code(char letter)
  #define MAX_LEVENSHTEIN_STRLEN		255
  
  static int levenshtein_internal(text *s, text *t,
! 					 int ins_c, int del_c, int sub_c);
  
  
  /*
--- 94,100 ----
  #define MAX_LEVENSHTEIN_STRLEN		255
  
  static int levenshtein_internal(text *s, text *t,
! 					 int ins_c, int del_c, int sub_c, int max_d);
  
  
  /*
***************
*** 202,211 **** rest_of_char_same(const char *s1, const char *s2, int len)
   *						  between supplied strings. Generally
   *						  (1, 1, 1) penalty costs suffices common
   *						  cases, but your mileage may vary.
   */
  static int
  levenshtein_internal(text *s, text *t,
! 					 int ins_c, int del_c, int sub_c)
  {
  	int			m,
  				n,
--- 204,217 ----
   *						  between supplied strings. Generally
   *						  (1, 1, 1) penalty costs suffices common
   *						  cases, but your mileage may vary.
+  *						  Returns accurate value if max_d < 0 or
+  *						  actual distance is less or equal than
+  *						  max_d, otherwise returns value greater
+  *						  than max_d.
   */
  static int
  levenshtein_internal(text *s, text *t,
! 					 int ins_c, int del_c, int sub_c, int max_d)
  {
  	int			m,
  				n,
***************
*** 219,224 **** levenshtein_internal(text *s, text *t,
--- 225,233 ----
  	const char *s_data;
  	const char *t_data;
  	const char *y;
+ 	const char *prev_x = NULL;
+ 	int        min_i = 0, max_i = 0, d;
+ 	int        delta = 0, min_d = 0, theor_max_d;
  
  	/* Extract a pointer to the actual character data. */
  	s_data = VARDATA_ANY(s);
***************
*** 240,245 **** levenshtein_internal(text *s, text *t,
--- 249,268 ----
  		return m * del_c;
  
  	/*
+ 	 * There is theoretical maximum distance based of string lengths. It
+ 	 * represents the case, when no characters are matching. If max_d
+ 	 * reaches this value than we can use accurate calculation of distance
+ 	 * which is faster in this case.
+ 	 */
+ 	if (max_d >= 0)
+ 	{
+ 		theor_max_d = Min(m*del_c + n*ins_c, (m > n) ?
+ 			(n * sub_c + (m - n) * del_c):(m * sub_c + (n - m) * ins_c)) - 1;
+ 		if (max_d >= theor_max_d)
+ 			max_d = -1;
+ 	}
+ 
+ 	/*
  	 * For security concerns, restrict excessive CPU+RAM usage. (This
  	 * implementation uses O(m) memory and has O(mn) complexity.)
  	 */
***************
*** 251,256 **** levenshtein_internal(text *s, text *t,
--- 274,296 ----
  						MAX_LEVENSHTEIN_STRLEN)));
  
  	/*
+ 	 * We can find the minimal distance by the difference of string lengths.
+ 	 */
+ 	if (max_d >= 0)
+ 	{
+ 		delta = m - n;
+ 		if (delta > 0)
+ 			min_d = delta * del_c;
+ 		else if (delta < 0)
+ 			min_d = - delta * ins_c;
+ 		else
+ 			min_d = 0;
+ 
+ 		if (min_d > max_d)
+ 			return max_d + 1;
+ 	}
+ 
+ 	/*
  	 * In order to avoid calling pg_mblen() repeatedly on each character in s,
  	 * we cache all the lengths before starting the main loop -- but if all the
  	 * characters in both strings are single byte, then we skip this and use
***************
*** 298,309 **** levenshtein_internal(text *s, text *t,
  	 */
  	for (i = 0; i < m; i++)
  		prev[i] = i * del_c;
  
  	/* Loop through rows of the notional array */
  	for (y = t_data, j = 1; j < n; j++)
  	{
  		int		   *temp;
- 		const char *x = s_data;
  		int			y_char_len = n != t_bytes + 1 ? pg_mblen(y) : 1;
  
  		/*
--- 338,372 ----
  	 */
  	for (i = 0; i < m; i++)
  		prev[i] = i * del_c;
+ 	/*
+ 	 * If we have limitation of max_d, than we'll maintain [min_i; max_i]
+ 	 * interval, which bound cells, where sum of cell value and smallest
+ 	 * possible residual cost is less or equal to max_d (we don't include
+ 	 * 0 index into this interval). Residual cost represent cost of insertions
+ 	 * or deletions for moving to diagonal, which containing bottom right cell.
+ 	 * The sum value saves important property of original matrix, that this
+ 	 * sum for cell always greater or equal than such sums for cells, which are
+ 	 * used for it's calculation. That's why this sum can be used for bound
+ 	 * interval.
+ 	 */
+ 	if (max_d >= 0)
+ 	{
+ 		min_i = 1;
+ 		max_i = 1;
+ 		while (max_i < m && prev[max_i] +
+ 			((delta - max_i > 0) ? (delta - max_i) * del_c :
+ 			(-delta + max_i) * ins_c) <= max_d)
+ 		{
+ 			max_i++;
+ 		}
+ 		max_i--;
+ 		prev_x = s_data;
+ 	}
  
  	/* Loop through rows of the notional array */
  	for (y = t_data, j = 1; j < n; j++)
  	{
  		int		   *temp;
  		int			y_char_len = n != t_bytes + 1 ? pg_mblen(y) : 1;
  
  		/*
***************
*** 313,378 **** levenshtein_internal(text *s, text *t,
  		curr[0] = j * ins_c;
  
  		/*
! 		 * This inner loop is critical to performance, so we include a
  		 * fast-path to handle the (fairly common) case where no multibyte
  		 * characters are in the mix.  The fast-path is entitled to assume
  		 * that if s_char_len is not initialized then BOTH strings contain
  		 * only single-byte characters.
  		 */
! 		if (s_char_len != NULL)
  		{
! 			for (i = 1; i < m; i++)
! 			{
! 				int			ins;
! 				int			del;
! 				int			sub;
! 				int			x_char_len = s_char_len[i - 1];
  
! 				/*
! 				 * Calculate costs for insertion, deletion, and substitution.
! 				 *
! 				 * When calculating cost for substitution, we compare the last
! 				 * character of each possibly-multibyte character first,
! 				 * because that's enough to rule out most mis-matches.  If we
! 				 * get past that test, then we compare the lengths and the
! 				 * remaining bytes.
! 				 */
! 				ins = prev[i] + ins_c;
! 				del = curr[i - 1] + del_c;
! 				if (x[x_char_len-1] == y[y_char_len-1]
! 					&& x_char_len == y_char_len &&
! 					(x_char_len == 1 || rest_of_char_same(x, y, x_char_len)))
! 					sub = prev[i - 1];
! 				else
! 					sub = prev[i - 1] + sub_c;
  
! 				/* Take the one with minimum cost. */
! 				curr[i] = Min(ins, del);
! 				curr[i] = Min(curr[i], sub);
  
! 				/* Point to next character. */
! 				x += x_char_len;
  			}
! 		}
! 		else
! 		{
! 			for (i = 1; i < m; i++)
  			{
! 				int			ins;
! 				int			del;
! 				int			sub;
  
! 				/* Calculate costs for insertion, deletion, and substitution. */
! 				ins = prev[i] + ins_c;
! 				del = curr[i - 1] + del_c;
! 				sub = prev[i - 1] + ((*x == *y) ? 0 : sub_c);
  
! 				/* Take the one with minimum cost. */
! 				curr[i] = Min(ins, del);
! 				curr[i] = Min(curr[i], sub);
  
! 				/* Point to next character. */
! 				x++;
  			}
  		}
  
--- 376,525 ----
  		curr[0] = j * ins_c;
  
  		/*
! 		 * This inner loop is critical to performance, that's why we
! 		 * handle case without max_d limitation separatetly. Also we include a
  		 * fast-path to handle the (fairly common) case where no multibyte
  		 * characters are in the mix.  The fast-path is entitled to assume
  		 * that if s_char_len is not initialized then BOTH strings contain
  		 * only single-byte characters.
  		 */
! 		if (max_d < 0)
  		{
! 			const char *x = s_data;
! 			/*
! 			 * First cell must increment sequentially, as we're on the j'th row of
! 			 * the (m+1)x(n+1) array.
! 			 */
! 			curr[0] = j * ins_c;
  
! 			if (s_char_len != NULL)
! 			{
! 				for (i = 1; i < m; i++)
! 				{
! 					int			ins;
! 					int			del;
! 					int			sub;
! 					int			x_char_len = s_char_len[i - 1];
! 
! 					/*
! 					 * Calculate costs for insertion, deletion, and substitution.
! 					 *
! 					 * When calculating cost for substitution, we compare the last
! 					 * character of each possibly-multibyte character first,
! 					 * because that's enough to rule out most mis-matches.  If we
! 					 * get past that test, then we compare the lengths and the
! 					 * remaining bytes.
! 					 */
! 					ins = prev[i] + ins_c;
! 					del = curr[i - 1] + del_c;
! 					if (x[x_char_len-1] == y[y_char_len-1]
! 						&& x_char_len == y_char_len &&
! 						(x_char_len == 1 || rest_of_char_same(x, y, x_char_len)))
! 						sub = prev[i - 1];
! 					else
! 						sub = prev[i - 1] + sub_c;
  
! 					/* Take the one with minimum cost. */
! 					curr[i] = Min(ins, del);
! 					curr[i] = Min(curr[i], sub);
  
! 					/* Point to next character. */
! 					x += x_char_len;
! 				}
  			}
! 			else
  			{
! 				for (i = 1; i < m; i++)
! 				{
! 					int			ins;
! 					int			del;
! 					int			sub;
  
! 					/* Calculate costs for insertion, deletion, and substitution. */
! 					ins = prev[i] + ins_c;
! 					del = curr[i - 1] + del_c;
! 					sub = prev[i - 1] + ((*x == *y) ? 0 : sub_c);
  
! 					/* Take the one with minimum cost. */
! 					curr[i] = Min(ins, del);
! 					curr[i] = Min(curr[i], sub);
  
! 					/* Point to next character. */
! 					x++;
! 				}
! 			}
! 		}else{
! 			const char *x = prev_x;
! 
! 			/*
! 			 * Fill the first cell of the row taking care about it's position
! 			 * relatively last cell's diagnal.
! 			 */
! 			curr[0] = j * ins_c;
! 			
! 			if (s_char_len != NULL)
! 			{
! 				for (i = min_i; i < m && i < max_i + 2; i++)
! 				{
! 					int	x_char_len = s_char_len[i - 1];
! 					/*
! 					 * Take minimum costs for insertion, deletion, and
! 					 * substitution when corresponding previous cell doesn't
! 					 * exceed bounds where value was less or equal than max_d.
! 					 */
! 					d = max_d + 1;
! 					if (i <= max_i)
! 						d = Min(prev[i] + ins_c, d);
! 					if (i == 1 || i > min_i)
! 					{
! 						d = Min(curr[i - 1] + del_c, d);
! 						d = Min(prev[i - 1] + ((x[x_char_len-1] == y[y_char_len-1]
! 							&& x_char_len == y_char_len
! 							&& (x_char_len == 1 || rest_of_char_same(x, y, x_char_len)))
! 							? 0 : sub_c), d);
! 					}
! 					curr[i] = d;
! 					x += x_char_len;
! 				}
! 			}
! 			else
! 			{
! 				for (i = min_i; i < m && i < max_i + 2; i++)
! 				{
! 					/*
! 					 * Take minimum costs for insertion, deletion, and
! 					 * substitution when corresponding previous cell doesn't
! 					 * exceed bounds where value was less or equal than max_d.
! 					 */
! 					d = max_d + 1;
! 					if (i <= max_i)
! 						d = Min(prev[i] + ins_c, d);
! 					if (i == 1 || i > min_i)
! 					{
! 						d = Min(curr[i - 1] + del_c, d);
! 						d = Min(prev[i - 1] + (*x == *y ? 0 : sub_c), d);
! 					}
! 					curr[i] = d;
! 					x++;
! 				}
! 			}
! 			if (max_i < m)
! 				max_i++;
! 			while (min_i <= max_i && curr[min_i] + ((delta - min_i + j > 0) ?
! 				(delta - min_i + j) * del_c : (-delta + min_i - j) * ins_c) > max_d)
! 			{
! 				if (s_char_len != NULL)
! 					prev_x += s_char_len[min_i - 1];
! 				else
! 					prev_x++;
! 				min_i++;
! 			}
! 			if (min_i > max_i)
! 				break;
! 			while (curr[max_i] + ((delta - max_i + j > 0) ?	(delta - max_i + j) * del_c :
! 				(-delta + max_i - j) * ins_c) > max_d)
! 			{
! 				max_i--;
  			}
  		}
  
***************
*** 386,391 **** levenshtein_internal(text *s, text *t,
--- 533,545 ----
  	}
  
  	/*
+ 	 * If we have limitation of max_d and haven't less or equal, than max_d
+ 	 * value in the last row than we should return max_d + 1.
+ 	 */
+ 	if (max_d >= 0 && min_i > max_i)
+ 		return max_d + 1;
+ 
+ 	/*
  	 * Because the final value was swapped from the previous row to the
  	 * current row, that's where we'll find it.
  	 */
***************
*** 403,409 **** levenshtein_with_costs(PG_FUNCTION_ARGS)
  	int			del_c = PG_GETARG_INT32(3);
  	int			sub_c = PG_GETARG_INT32(4);
  
! 	PG_RETURN_INT32(levenshtein_internal(src, dst, ins_c, del_c, sub_c));
  }
  
  
--- 557,563 ----
  	int			del_c = PG_GETARG_INT32(3);
  	int			sub_c = PG_GETARG_INT32(4);
  
! 	PG_RETURN_INT32(levenshtein_internal(src, dst, ins_c, del_c, sub_c, -1));
  }
  
  
***************
*** 414,420 **** levenshtein(PG_FUNCTION_ARGS)
  	text	   *src = PG_GETARG_TEXT_PP(0);
  	text	   *dst = PG_GETARG_TEXT_PP(1);
  
! 	PG_RETURN_INT32(levenshtein_internal(src, dst, 1, 1, 1));
  }
  
  
--- 568,601 ----
  	text	   *src = PG_GETARG_TEXT_PP(0);
  	text	   *dst = PG_GETARG_TEXT_PP(1);
  
! 	PG_RETURN_INT32(levenshtein_internal(src, dst, 1, 1, 1, -1));
! }
! 
! 
! PG_FUNCTION_INFO_V1(levenshtein_less_equal_with_costs);
! Datum
! levenshtein_less_equal_with_costs(PG_FUNCTION_ARGS)
! {
! 	text	   *src = PG_GETARG_TEXT_PP(0);
! 	text	   *dst = PG_GETARG_TEXT_PP(1);
! 	int			ins_c = PG_GETARG_INT32(2);
! 	int			del_c = PG_GETARG_INT32(3);
! 	int			sub_c = PG_GETARG_INT32(4);
! 	int			max_d = PG_GETARG_INT32(5);
! 
! 	PG_RETURN_INT32(levenshtein_internal(src, dst, ins_c, del_c, sub_c, max_d));
! }
! 
! 
! PG_FUNCTION_INFO_V1(levenshtein_less_equal);
! Datum
! levenshtein_less_equal(PG_FUNCTION_ARGS)
! {
! 	text	   *src = PG_GETARG_TEXT_PP(0);
! 	text	   *dst = PG_GETARG_TEXT_PP(1);
! 	int			max_d = PG_GETARG_INT32(2);
! 
! 	PG_RETURN_INT32(levenshtein_internal(src, dst, 1, 1, 1, max_d));
  }
  
  
*** a/contrib/fuzzystrmatch/fuzzystrmatch.sql.in
--- b/contrib/fuzzystrmatch/fuzzystrmatch.sql.in
***************
*** 11,16 **** CREATE OR REPLACE FUNCTION levenshtein (text,text,int,int,int) RETURNS int
--- 11,24 ----
  AS 'MODULE_PATHNAME','levenshtein_with_costs'
  LANGUAGE C IMMUTABLE STRICT;
  
+ CREATE OR REPLACE FUNCTION levenshtein_less_equal (text,text,int) RETURNS int
+ AS 'MODULE_PATHNAME','levenshtein_less_equal'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE OR REPLACE FUNCTION levenshtein_less_equal (text,text,int,int,int,int) RETURNS int
+ AS 'MODULE_PATHNAME','levenshtein_less_equal_with_costs'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
  CREATE OR REPLACE FUNCTION metaphone (text,int) RETURNS text
  AS 'MODULE_PATHNAME','metaphone'
  LANGUAGE C IMMUTABLE STRICT;
*** a/doc/src/sgml/fuzzystrmatch.sgml
--- b/doc/src/sgml/fuzzystrmatch.sgml
***************
*** 84,89 **** SELECT * FROM s WHERE difference(s.nm, 'john') &gt; 2;
--- 84,91 ----
  <synopsis>
  levenshtein(text source, text target, int ins_cost, int del_cost, int sub_cost) returns int
  levenshtein(text source, text target) returns int
+ levenshtein_less_equal(text source, text target, int ins_cost, int del_cost, int sub_cost, int max_d) returns int
+ levenshtein_less_equal(text source, text target, int max_d) returns int
  </synopsis>
  
    <para>
***************
*** 92,97 **** levenshtein(text source, text target) returns int
--- 94,104 ----
     specify how much to charge for a character insertion, deletion, or
     substitution, respectively.  You can omit the cost parameters, as in
     the second version of the function; in that case they all default to 1.
+    <literal>levenshtein_less_equal</literal> is accelerated version of
+    levenshtein functon for low values of distance. If actual distance
+    is less or equal then max_d, then <literal>levenshtein_less_equal</literal>
+    returns accurate value of it. Otherwise this function returns value
+    which is greater than max_d.
    </para>
  
    <para>
***************
*** 110,115 **** test=# SELECT levenshtein('GUMBO', 'GAMBOL', 2,1,1);
--- 117,134 ----
  -------------
             3
  (1 row)
+ 
+ test=# SELECT levenshtein_less_equal('extensive', 'exhaustive',2);
+  levenshtein_less_equal
+ ------------------------
+                       3
+ (1 row)
+ 
+ test=# SELECT levenshtein_less_equal('extensive', 'exhaustive',4);
+  levenshtein_less_equal
+ ------------------------
+                       4
+ (1 row)
  </screen>
   </sect2>
  
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to