~ubuntu-branches/ubuntu/maverick/imdbpy/maverick

« back to all changes in this revision

Viewing changes to imdb/parser/sql/cutils.c

  • Committer: Bazaar Package Importer
  • Author(s): Ana Beatriz Guerrero Lopez
  • Date: 2009-09-13 01:00:12 UTC
  • mfrom: (1.1.11 upstream) (3.1.7 sid)
  • Revision ID: james.westby@ubuntu.com-20090913010012-buaumiky0oxs11ku
Tags: 4.2-1
* New upstream release.
* Update patch no_install_doc_or_stuff_in_etc.
* Update to standards-Version 3.8.3.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * cutils.c module.
 
3
 *
 
4
 * Miscellaneous functions to speed up the IMDbPY package.
 
5
 *
 
6
 * Contents:
 
7
 * - pyratcliff():
 
8
 *   Function that implements the Ratcliff-Obershelp comparison
 
9
 *   amongst Python strings.
 
10
 *
 
11
 * - pysoundex():
 
12
 *   Return a soundex code string, for the given string.
 
13
 *
 
14
 * Copyright 2004-2009 Davide Alberani <da@erlug.linux.it>
 
15
 * Released under the GPL license.
 
16
 *
 
17
 * NOTE: The Ratcliff-Obershelp part was heavily based on code from the
 
18
 * "simil" Python module.
 
19
 * The "simil" module is copyright of Luca Montecchiani <cbm64 _at_ inwind.it>
 
20
 * and can be found here: http://spazioinwind.libero.it/montecchiani/
 
21
 * It was released under the GPL license; original comments are leaved
 
22
 * below.
 
23
 *
 
24
 */
 
25
 
 
26
 
 
27
/*========== Ratcliff-Obershelp ==========*/
 
28
/*****************************************************************************
 
29
 *
 
30
 * Stolen code from :
 
31
 *
 
32
 * [Python-Dev] Why is soundex marked obsolete?
 
33
 * by Eric S. Raymond [4]esr@thyrsus.com
 
34
 * on Sun, 14 Jan 2001 14:09:01 -0500
 
35
 *
 
36
 *****************************************************************************/
 
37
 
 
38
/*****************************************************************************
 
39
 *
 
40
 * Ratcliff-Obershelp common-subpattern similarity.
 
41
 *
 
42
 * This code first appeared in a letter to the editor in Doctor
 
43
 * Dobbs's Journal, 11/1988.  The original article on the algorithm,
 
44
 * "Pattern Matching by Gestalt" by John Ratcliff, had appeared in the
 
45
 * July 1988 issue (#181) but the algorithm was presented in assembly.
 
46
 * The main drawback of the Ratcliff-Obershelp algorithm is the cost
 
47
 * of the pairwise comparisons.  It is significantly more expensive
 
48
 * than stemming, Hamming distance, soundex, and the like.
 
49
 *
 
50
 * Running time quadratic in the data size, memory usage constant.
 
51
 *
 
52
 *****************************************************************************/
 
53
 
 
54
#include <Python.h>
 
55
 
 
56
#define DONTCOMPARE_NULL    0.0
 
57
#define DONTCOMPARE_SAME    1.0
 
58
#define COMPARE             2.0
 
59
#define STRING_MAXLENDIFFER 0.7
 
60
 
 
61
/* As of 05 Mar 2008, the longest title is ~600 chars. */
 
62
#define MXLINELEN   1023
 
63
 
 
64
#define MAX(a,b) ((a) > (b) ? (a) : (b))
 
65
 
 
66
 
 
67
//*****************************************
 
68
// preliminary check....
 
69
//*****************************************
 
70
static float
 
71
strings_check(char const *s, char const *t)
 
72
{
 
73
    float threshold;    // lenght difference
 
74
    int s_len = strlen(s);    // length of s
 
75
    int t_len = strlen(t);    // length of t
 
76
 
 
77
    // NULL strings ?
 
78
    if ((t_len * s_len) == 0)
 
79
        return (DONTCOMPARE_NULL);
 
80
 
 
81
    // the same ?
 
82
    if (strcmp(s, t) == 0)
 
83
        return (DONTCOMPARE_SAME);
 
84
 
 
85
    // string lenght difference threshold
 
86
    // we don't want to compare too different lenght strings ;)
 
87
    if (s_len < t_len)
 
88
        threshold = (float) s_len / (float) t_len;
 
89
    else
 
90
        threshold = (float) t_len / (float) s_len;
 
91
    if (threshold < STRING_MAXLENDIFFER)
 
92
        return (DONTCOMPARE_NULL);
 
93
 
 
94
    // proceed
 
95
    return (COMPARE);
 
96
}
 
97
 
 
98
 
 
99
static int
 
100
RatcliffObershelp(char *st1, char *end1, char *st2, char *end2)
 
101
{
 
102
    register char *a1, *a2;
 
103
    char *b1, *b2;
 
104
    char *s1 = st1, *s2 = st2;    /* initializations are just to pacify GCC */
 
105
    short max, i;
 
106
 
 
107
    if (end1 <= st1 || end2 <= st2)
 
108
        return (0);
 
109
    if (end1 == st1 + 1 && end2 == st2 + 1)
 
110
        return (0);
 
111
 
 
112
    max = 0;
 
113
    b1 = end1;
 
114
    b2 = end2;
 
115
 
 
116
    for (a1 = st1; a1 < b1; a1++) {
 
117
        for (a2 = st2; a2 < b2; a2++) {
 
118
            if (*a1 == *a2) {
 
119
                /* determine length of common substring */
 
120
                for (i = 1; a1[i] && (a1[i] == a2[i]); i++)
 
121
                    continue;
 
122
                if (i > max) {
 
123
                    max = i;
 
124
                    s1 = a1;
 
125
                    s2 = a2;
 
126
                    b1 = end1 - max;
 
127
                    b2 = end2 - max;
 
128
                }
 
129
            }
 
130
        }
 
131
    }
 
132
    if (!max)
 
133
        return (0);
 
134
    max += RatcliffObershelp(s1 + max, end1, s2 + max, end2);    /* rhs */
 
135
    max += RatcliffObershelp(st1, s1, st2, s2);    /* lhs */
 
136
    return max;
 
137
}
 
138
 
 
139
 
 
140
static float
 
141
ratcliff(char *s1, char *s2)
 
142
/* compute Ratcliff-Obershelp similarity of two strings */
 
143
{
 
144
    int l1, l2;
 
145
    float res;
 
146
 
 
147
    // preliminary tests
 
148
    res = strings_check(s1, s2);
 
149
    if (res != COMPARE)
 
150
        return(res);
 
151
 
 
152
    l1 = strlen(s1);
 
153
    l2 = strlen(s2);
 
154
 
 
155
    return 2.0 * RatcliffObershelp(s1, s1 + l1, s2, s2 + l2) / (l1 + l2);
 
156
}
 
157
 
 
158
 
 
159
/* Change a string to lowercase. */
 
160
static void
 
161
strtolower(char *s1)
 
162
{
 
163
    int i;
 
164
    for (i=0; i < strlen(s1); i++) s1[i] = tolower(s1[i]);
 
165
}
 
166
 
 
167
 
 
168
/* Ratcliff-Obershelp for two python strings; returns a python float. */
 
169
static PyObject*
 
170
pyratcliff(PyObject *self, PyObject *pArgs)
 
171
{
 
172
    char *s1 = NULL;
 
173
    char *s2 = NULL;
 
174
    PyObject *discard = NULL;
 
175
    char s1copy[MXLINELEN+1];
 
176
    char s2copy[MXLINELEN+1];
 
177
 
 
178
    /* The optional PyObject parameter is here to be compatible
 
179
     * with the pure python implementation, which uses a
 
180
     * difflib.SequenceMatcher object. */
 
181
    if (!PyArg_ParseTuple(pArgs, "ss|O", &s1, &s2, &discard))
 
182
        return NULL;
 
183
 
 
184
    strncpy(s1copy, s1, MXLINELEN);
 
185
    strncpy(s2copy, s2, MXLINELEN);
 
186
    /* Work on copies. */
 
187
    strtolower(s1copy);
 
188
    strtolower(s2copy);
 
189
 
 
190
    return Py_BuildValue("f", ratcliff(s1copy, s2copy));
 
191
}
 
192
 
 
193
 
 
194
/*========== soundex ==========*/
 
195
/* Max length of the soundex code to output (an uppercase char and
 
196
 * _at most_ 4 digits). */
 
197
#define SOUNDEX_LEN 5
 
198
 
 
199
/* Group Number Lookup Table  */
 
200
static char soundTable[26] =
 
201
{ 0 /* A */, '1' /* B */, '2' /* C */, '3' /* D */, 0 /* E */, '1' /* F */,
 
202
 '2' /* G */, 0 /* H */, 0 /* I */, '2' /* J */, '2' /* K */, '4' /* L */,
 
203
 '5' /* M */, '5' /* N */, 0 /* O */, '1' /* P */, '2' /* Q */, '6' /* R */,
 
204
 '2' /* S */, '3' /* T */, 0 /* U */, '1' /* V */, 0 /* W */, '2' /* X */,
 
205
  0 /* Y */, '2' /* Z */};
 
206
 
 
207
static PyObject*
 
208
pysoundex(PyObject *self, PyObject *pArgs)
 
209
{
 
210
    int i, j, n;
 
211
    char *s = NULL;
 
212
    char word[MXLINELEN+1];
 
213
    char soundCode[SOUNDEX_LEN+1];
 
214
    char c;
 
215
 
 
216
    if (!PyArg_ParseTuple(pArgs, "s", &s))
 
217
        return NULL;
 
218
 
 
219
    j = 0;
 
220
    n = strlen(s);
 
221
 
 
222
    /* Convert to uppercase and exclude non-ascii chars. */
 
223
    for (i = 0; i < n; i++) {
 
224
        c = toupper(s[i]);
 
225
        if (c < 91 && c > 64) {
 
226
            word[j] = c;
 
227
            j++;
 
228
        }
 
229
    }
 
230
    word[j] = '\0';
 
231
 
 
232
    n = strlen(word);
 
233
    if (n == 0) {
 
234
        /* If the string is empty, returns None. */
 
235
        return Py_BuildValue("");
 
236
    }
 
237
    soundCode[0] = word[0];
 
238
 
 
239
    /* Build the soundCode string. */
 
240
    j = 1;
 
241
    for (i = 1; j < SOUNDEX_LEN && i < n; i++) {
 
242
        c = soundTable[(word[i]-65)];
 
243
        /* Compact zeroes and equal consecutive digits ("12234112"->"123412") */
 
244
        if (c != 0 && c != soundCode[j-1]) {
 
245
                soundCode[j++] = c;
 
246
        }
 
247
    }
 
248
    soundCode[j] = '\0';
 
249
 
 
250
    return Py_BuildValue("s", soundCode);
 
251
}
 
252
 
 
253
 
 
254
static PyMethodDef cutils_methods[] = {
 
255
    {"ratcliff", pyratcliff,
 
256
        METH_VARARGS, "Ratcliff-Obershelp similarity."},
 
257
    {"soundex", pysoundex,
 
258
        METH_VARARGS, "Soundex code for strings."},
 
259
    {NULL}
 
260
};
 
261
 
 
262
 
 
263
void
 
264
initcutils(void)
 
265
{
 
266
    Py_InitModule("cutils", cutils_methods);
 
267
}
 
268
 
 
269