/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/_patiencediff_c.c

  • Committer: Jelmer Vernooij
  • Date: 2018-11-21 03:39:28 UTC
  • mto: This revision was merged to the branch mainline in revision 7206.
  • Revision ID: jelmer@jelmer.uk-20181121033928-ck4sb5zfdwosw35b
Fix test.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 Copyright (C) 2007, 2010 Canonical Ltd
 
3
 
 
4
 This program is free software; you can redistribute it and/or modify
 
5
 it under the terms of the GNU General Public License as published by
 
6
 the Free Software Foundation; either version 2 of the License, or
 
7
 (at your option) any later version.
 
8
 
 
9
 This program is distributed in the hope that it will be useful,
 
10
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 GNU General Public License for more details.
 
13
 
 
14
 You should have received a copy of the GNU General Public License
 
15
 along with this program; if not, write to the Free Software
 
16
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
17
 
 
18
 Function equate_lines based on bdiff.c from Mercurial.
 
19
   Copyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>
 
20
 
 
21
 Functions unique_lcs/recurse_matches based on _patiencediff_py.py.
 
22
   Copyright (C) 2005 Bram Cohen, Copyright (C) 2005, 2006 Canonical Ltd
 
23
*/
 
24
 
 
25
 
 
26
#include <stdlib.h>
 
27
#include <string.h>
 
28
#include <Python.h>
 
29
 
 
30
#include "python-compat.h"
 
31
 
 
32
 
 
33
#if defined(__GNUC__)
 
34
#   define inline __inline__
 
35
#elif defined(_MSC_VER)
 
36
#   define inline __inline
 
37
#else
 
38
#   define inline
 
39
#endif
 
40
 
 
41
 
 
42
#define MIN(a, b) (((a) > (b)) ? (b) : (a))
 
43
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
 
44
 
 
45
 
 
46
#define SENTINEL -1
 
47
 
 
48
 
 
49
enum {
 
50
    OP_EQUAL = 0,
 
51
    OP_INSERT,
 
52
    OP_DELETE,
 
53
    OP_REPLACE
 
54
};
 
55
 
 
56
 
 
57
/* values from this array need to correspont to the order of the enum above */
 
58
static char *opcode_names[] = {
 
59
    "equal",
 
60
    "insert",
 
61
    "delete",
 
62
    "replace",
 
63
};
 
64
 
 
65
 
 
66
struct line {
 
67
    long hash;         /* hash code of the string/object */
 
68
    Py_ssize_t next;   /* next line from the same equivalence class */
 
69
    Py_ssize_t equiv;  /* equivalence class */
 
70
    PyObject *data;
 
71
};
 
72
 
 
73
 
 
74
struct bucket {
 
75
    Py_ssize_t a_head;  /* first item in `a` from this equivalence class */
 
76
    Py_ssize_t a_count;
 
77
    Py_ssize_t b_head;  /* first item in `b` from this equivalence class */
 
78
    Py_ssize_t b_count;
 
79
    Py_ssize_t a_pos;
 
80
    Py_ssize_t b_pos;
 
81
};
 
82
 
 
83
 
 
84
struct hashtable {
 
85
    Py_ssize_t last_a_pos;
 
86
    Py_ssize_t last_b_pos;
 
87
    Py_ssize_t size;
 
88
    struct bucket *table;
 
89
};
 
90
 
 
91
struct matching_line {
 
92
    Py_ssize_t a;     /* index of the line in `a` */
 
93
    Py_ssize_t b;     /* index of the line in `b` */
 
94
};
 
95
 
 
96
 
 
97
struct matching_block {
 
98
    Py_ssize_t a;     /* index of the first line in `a` */
 
99
    Py_ssize_t b;     /* index of the first line in `b` */
 
100
    Py_ssize_t len;   /* length of the block */
 
101
};
 
102
 
 
103
 
 
104
struct matching_blocks {
 
105
    struct matching_block *matches;
 
106
    Py_ssize_t count;
 
107
};
 
108
 
 
109
 
 
110
struct opcode {
 
111
    int tag;
 
112
    Py_ssize_t i1;
 
113
    Py_ssize_t i2;
 
114
    Py_ssize_t j1;
 
115
    Py_ssize_t j2;
 
116
};
 
117
 
 
118
 
 
119
typedef struct {
 
120
    PyObject_HEAD
 
121
    Py_ssize_t asize;
 
122
    Py_ssize_t bsize;
 
123
    struct line *a;
 
124
    struct line *b;
 
125
    struct hashtable hashtable;
 
126
    Py_ssize_t *backpointers;
 
127
} PatienceSequenceMatcher;
 
128
 
 
129
 
 
130
static inline Py_ssize_t
 
131
bisect_left(Py_ssize_t *list, Py_ssize_t item, Py_ssize_t lo, Py_ssize_t hi)
 
132
{
 
133
    while (lo < hi) {
 
134
        Py_ssize_t mid = lo / 2 + hi / 2 + (lo % 2 + hi % 2) / 2;
 
135
        if (list[mid] < item)
 
136
            lo = mid + 1;
 
137
        else
 
138
            hi = mid;
 
139
    }
 
140
    return lo;
 
141
}
 
142
 
 
143
 
 
144
static inline int
 
145
compare_lines(struct line *a, struct line *b)
 
146
{
 
147
    return ((a->hash != b->hash)
 
148
            || PyObject_RichCompareBool(a->data, b->data, Py_EQ) == 0);
 
149
}
 
150
 
 
151
 
 
152
static inline int
 
153
find_equivalence_class(struct bucket *hashtable, Py_ssize_t hsize,
 
154
                       struct line *lines, struct line *ref_lines,
 
155
                       Py_ssize_t i)
 
156
{
 
157
    Py_ssize_t j;
 
158
    for (j = lines[i].hash & hsize; hashtable[j].b_head != SENTINEL; j = (j + 1) & hsize) {
 
159
        if (!compare_lines(lines + i, ref_lines + hashtable[j].b_head)) {
 
160
            break;
 
161
        }
 
162
    }
 
163
    return j;
 
164
}
 
165
 
 
166
 
 
167
static int
 
168
equate_lines(struct hashtable *result,
 
169
             struct line *lines_a, struct line *lines_b,
 
170
             Py_ssize_t asize, Py_ssize_t bsize)
 
171
{
 
172
    Py_ssize_t i, j, hsize;
 
173
    struct bucket *hashtable;
 
174
 
 
175
    /* check for overflow, we need the table to be at least bsize+1 */
 
176
    if (bsize == PY_SSIZE_T_MAX) {
 
177
        PyErr_SetNone(PyExc_OverflowError);
 
178
        return 0;
 
179
    }
 
180
 
 
181
    /* build a hash table of the next highest power of 2 */
 
182
    hsize = 1;
 
183
    while (hsize < bsize + 1)
 
184
        hsize *= 2;
 
185
 
 
186
    /* can't be 0 */
 
187
    hashtable = (struct bucket *)malloc(sizeof(struct bucket) * hsize);
 
188
    if (hashtable == NULL) {
 
189
        PyErr_NoMemory();
 
190
        return 0;
 
191
    }
 
192
 
 
193
    /* initialise the hashtable */
 
194
    for (i = 0; i < hsize; i++) {
 
195
        hashtable[i].a_count = 0;
 
196
        hashtable[i].b_count = 0;
 
197
        hashtable[i].a_head = SENTINEL;
 
198
        hashtable[i].b_head = SENTINEL;
 
199
    }
 
200
    hsize--;
 
201
 
 
202
    /* add lines from lines_b to the hash table chains. iterating
 
203
       backwards so the matching lines are sorted to the linked list
 
204
       by the line number (because we are adding new lines to the
 
205
       head of the list) */
 
206
    for (i = bsize - 1; i >= 0; i--) {
 
207
        /* find the first hashtable entry, which is either empty or contains
 
208
           the same line as lines_b[i] */
 
209
        j = find_equivalence_class(hashtable, hsize, lines_b, lines_b, i);
 
210
 
 
211
        /* set the equivalence class */
 
212
        lines_b[i].equiv = j;
 
213
 
 
214
        /* add to the head of the equivalence class */
 
215
        lines_b[i].next = hashtable[j].b_head;
 
216
        hashtable[j].b_head = i;
 
217
        hashtable[j].b_count++;
 
218
    }
 
219
 
 
220
    /* match items from lines_a to their equivalence class in lines_b.
 
221
       again, iterating backwards for the right order of the linked lists */
 
222
    for (i = asize - 1; i >= 0; i--) {
 
223
        /* find the first hash entry, which is either empty or contains
 
224
           the same line as lines_a[i] */
 
225
        j = find_equivalence_class(hashtable, hsize, lines_a, lines_b, i);
 
226
 
 
227
        /* set the equivalence class, even if we are not interested in this
 
228
           line, because the values are not pre-filled */
 
229
        lines_a[i].equiv = j;
 
230
 
 
231
        /* we are not interested in lines which are not also in lines_b */
 
232
        if (hashtable[j].b_head == SENTINEL)
 
233
            continue;
 
234
 
 
235
        /* add to the head of the equivalence class */
 
236
        lines_a[i].next = hashtable[j].a_head;
 
237
        hashtable[j].a_head = i;
 
238
        hashtable[j].a_count++;
 
239
    }
 
240
 
 
241
    result->last_a_pos = -1;
 
242
    result->last_b_pos = -1;
 
243
    result->size = hsize + 1;
 
244
    result->table = hashtable;
 
245
 
 
246
    return 1;
 
247
}
 
248
 
 
249
 
 
250
 
 
251
/* Finds longest common subsequence of unique lines in a[alo:ahi] and
 
252
   b[blo:bhi].
 
253
   Parameter backpointers must have allocated memory for at least
 
254
   4 * (bhi - blo) ints. */
 
255
Py_ssize_t
 
256
unique_lcs(struct matching_line *answer,
 
257
           struct hashtable *hashtable, Py_ssize_t *backpointers,
 
258
           struct line *lines_a, struct line *lines_b,
 
259
           Py_ssize_t alo, Py_ssize_t blo, Py_ssize_t ahi, Py_ssize_t bhi)
 
260
{
 
261
    Py_ssize_t i, k, equiv, apos, bpos, norm_apos, norm_bpos, bsize, stacksize;
 
262
    Py_ssize_t *stacks, *lasts, *btoa;
 
263
    struct bucket *h;
 
264
 
 
265
    k = 0;
 
266
    stacksize = 0;
 
267
    bsize = bhi - blo;
 
268
    h = hashtable->table;
 
269
 
 
270
    /* "unpack" the allocated memory */
 
271
    stacks = backpointers + bsize;
 
272
    lasts = stacks + bsize;
 
273
    btoa = lasts + bsize;
 
274
 
 
275
    /* initialise the backpointers */
 
276
    for (i = 0; i < bsize; i++)
 
277
        backpointers[i] = SENTINEL;
 
278
 
 
279
    if (hashtable->last_a_pos == -1 || hashtable->last_a_pos > alo)
 
280
        for (i = 0; i < hashtable->size; i++)
 
281
            h[i].a_pos = h[i].a_head;
 
282
    hashtable->last_a_pos = alo;
 
283
 
 
284
    if (hashtable->last_b_pos == -1 || hashtable->last_b_pos > blo)
 
285
        for (i = 0; i < hashtable->size; i++)
 
286
            h[i].b_pos = h[i].b_head;
 
287
    hashtable->last_b_pos = blo;
 
288
 
 
289
    for (bpos = blo; bpos < bhi; bpos++) {
 
290
        equiv = lines_b[bpos].equiv;
 
291
 
 
292
        /* no lines in a or b  */
 
293
        if (h[equiv].a_count == 0 || h[equiv].b_count == 0)
 
294
            continue;
 
295
 
 
296
        /* find an unique line in lines_a that matches lines_b[bpos]
 
297
           if we find more than one line within the range alo:ahi,
 
298
           jump to the next line from lines_b immediately */
 
299
        apos = SENTINEL;
 
300
        /* loop through all lines in the linked list */
 
301
        for (i = h[equiv].a_pos; i != SENTINEL; i = lines_a[i].next) {
 
302
            /* the index is lower than alo, continue to the next line */
 
303
            if (i < alo) {
 
304
                h[equiv].a_pos = i;
 
305
                continue;
 
306
            }
 
307
            /* the index is higher than ahi, stop searching */
 
308
            if (i >= ahi)
 
309
                break;
 
310
            /* if the line is within our range, check if it's a duplicate */
 
311
            if (apos != SENTINEL)
 
312
                goto nextb;
 
313
            /* save index to the line */
 
314
            apos = i;
 
315
        }
 
316
        /* this line has no equivalent in lines_a[alo:ahi] */
 
317
        if (apos == SENTINEL)
 
318
            goto nextb;
 
319
 
 
320
        /* check for duplicates of this line in lines_b[blo:bhi] */
 
321
        /* loop through all lines in the linked list */
 
322
        for (i = h[equiv].b_pos; i != SENTINEL; i = lines_b[i].next) {
 
323
            /* the index is lower than blo, continue to the next line */
 
324
            if (i < blo) {
 
325
                h[equiv].b_pos = i;
 
326
                continue;
 
327
            }
 
328
            /* the index is higher than bhi, stop searching */
 
329
            if (i >= bhi)
 
330
                break;
 
331
            /* if this isn't the line with started with and it's within
 
332
               our range, it's a duplicate */
 
333
            if (i != bpos)
 
334
                goto nextb;
 
335
        }
 
336
 
 
337
        /* use normalised indexes ([0,ahi-alo) instead of [alo,ahi))
 
338
           for the patience sorting algorithm */
 
339
        norm_bpos = bpos - blo;
 
340
        norm_apos = apos - alo;
 
341
        btoa[norm_bpos] = norm_apos;
 
342
 
 
343
        /*
 
344
        Ok, how does this work...
 
345
 
 
346
        We have a list of matching lines from two lists, a and b. These
 
347
        matches are stored in variable `btoa`. As we are iterating over this
 
348
        table by bpos, the lines from b already form an increasing sequence.
 
349
        We need to "sort" also the lines from a using the patience sorting
 
350
        algorithm, ignoring the lines which would need to be swapped.
 
351
 
 
352
          http://en.wikipedia.org/wiki/Patience_sorting
 
353
 
 
354
        For each pair of lines, we need to place the line from a on either
 
355
        an existing pile that has higher value on the top or create a new
 
356
        pile. Variable `stacks` represents the tops of these piles and in
 
357
        variable `lasts` we store the lines from b, that correspond to the
 
358
        lines from a in `stacks`.
 
359
 
 
360
        Whenever we place a new line on top of a pile, we store a
 
361
        backpointer to the line (b) from top of the previous pile. This means
 
362
        that after the loop, variable `backpointers` will contain an index
 
363
        to the previous matching lines that forms an increasing sequence
 
364
        (over both indexes a and b) with the current matching lines. If
 
365
        either index a or b of the previous matching lines would be higher
 
366
        than indexes of the current one or if the indexes of the current
 
367
        one are 0, it will contain SENTINEL.
 
368
 
 
369
        To construct the LCS, we will just need to follow these backpointers
 
370
        from the top of the last pile and stop when we reach SENTINEL.
 
371
        */
 
372
 
 
373
        /* as an optimization, check if the next line comes at the end,
 
374
           because it usually does */
 
375
        if (stacksize && stacks[stacksize - 1] < norm_apos)
 
376
            k = stacksize;
 
377
        /* as an optimization, check if the next line comes right after
 
378
           the previous line, because usually it does */
 
379
        else if (stacksize && (stacks[k] < norm_apos) &&
 
380
                 (k == stacksize - 1 || stacks[k + 1] > norm_apos))
 
381
            k += 1;
 
382
        else
 
383
            k = bisect_left(stacks, norm_apos, 0, stacksize);
 
384
 
 
385
        if (k > 0)
 
386
            backpointers[norm_bpos] = lasts[k - 1];
 
387
 
 
388
        if (k < stacksize) {
 
389
            stacks[k] = norm_apos;
 
390
            lasts[k] = norm_bpos;
 
391
        }
 
392
        else {
 
393
            stacks[stacksize] = norm_apos;
 
394
            lasts[stacksize] = norm_bpos;
 
395
            stacksize += 1;
 
396
        }
 
397
 
 
398
 
 
399
nextb:
 
400
        ;
 
401
    }
 
402
 
 
403
    if (stacksize == 0)
 
404
        return 0;
 
405
 
 
406
    /* backtrace the structures to find the LCS */
 
407
    i = 0;
 
408
    k = lasts[stacksize - 1];
 
409
    while (k != SENTINEL) {
 
410
        answer[i].a = btoa[k];
 
411
        answer[i].b = k;
 
412
        k = backpointers[k];
 
413
        i++;
 
414
    }
 
415
 
 
416
    return i;
 
417
}
 
418
 
 
419
/* Adds a new line to the list of matching blocks, either extending the
 
420
   current block or adding a new one. */
 
421
static inline void
 
422
add_matching_line(struct matching_blocks *answer, Py_ssize_t a, Py_ssize_t b)
 
423
{
 
424
    Py_ssize_t last_index = answer->count - 1;
 
425
    if ((last_index >= 0) &&
 
426
        (a == answer->matches[last_index].a +
 
427
              answer->matches[last_index].len) &&
 
428
        (b == answer->matches[last_index].b +
 
429
              answer->matches[last_index].len)) {
 
430
        /* enlarge the last block */
 
431
        answer->matches[last_index].len++;
 
432
    }
 
433
    else {
 
434
        /* create a new block */
 
435
        last_index++;
 
436
        answer->matches[last_index].a = a;
 
437
        answer->matches[last_index].b = b;
 
438
        answer->matches[last_index].len = 1;
 
439
        answer->count++;
 
440
    }
 
441
}
 
442
 
 
443
 
 
444
static int
 
445
recurse_matches(struct matching_blocks *answer, struct hashtable *hashtable,
 
446
                Py_ssize_t *backpointers, struct line *a, struct line *b,
 
447
                Py_ssize_t alo, Py_ssize_t blo, Py_ssize_t ahi, Py_ssize_t bhi,
 
448
                int maxrecursion)
 
449
{
 
450
    int res;
 
451
    Py_ssize_t new, last_a_pos, last_b_pos, lcs_size, nahi, nbhi, i, apos, bpos;
 
452
    struct matching_line *lcs;
 
453
 
 
454
    if (maxrecursion < 0)
 
455
        return 1;
 
456
 
 
457
    if (alo == ahi || blo == bhi)
 
458
        return 1;
 
459
 
 
460
    new = 0;
 
461
    last_a_pos = alo - 1;
 
462
    last_b_pos = blo - 1;
 
463
 
 
464
    lcs = (struct matching_line *)malloc(sizeof(struct matching_line) * (bhi - blo));
 
465
    if (lcs == NULL)
 
466
        return 0;
 
467
 
 
468
    lcs_size = unique_lcs(lcs, hashtable, backpointers, a, b, alo, blo, ahi, bhi);
 
469
 
 
470
    /* recurse between lines which are unique in each file and match */
 
471
    for (i = lcs_size - 1; i >= 0; i--) {
 
472
        apos = alo + lcs[i].a;
 
473
        bpos = blo + lcs[i].b;
 
474
        if (last_a_pos + 1 != apos || last_b_pos + 1 != bpos) {
 
475
            res = recurse_matches(answer, hashtable,
 
476
                                  backpointers, a, b,
 
477
                                  last_a_pos + 1, last_b_pos + 1,
 
478
                                  apos, bpos, maxrecursion - 1);
 
479
            if (!res)
 
480
                goto error;
 
481
        }
 
482
        last_a_pos = apos;
 
483
        last_b_pos = bpos;
 
484
        add_matching_line(answer, apos, bpos);
 
485
        new = 1;
 
486
    }
 
487
 
 
488
    free(lcs);
 
489
    lcs = NULL;
 
490
 
 
491
    /* find matches between the last match and the end */
 
492
    if (new > 0) {
 
493
        res = recurse_matches(answer, hashtable,
 
494
                              backpointers, a, b,
 
495
                              last_a_pos + 1, last_b_pos + 1,
 
496
                              ahi, bhi, maxrecursion - 1);
 
497
        if (!res)
 
498
            goto error;
 
499
    }
 
500
 
 
501
 
 
502
    /* find matching lines at the very beginning */
 
503
    else if (a[alo].equiv == b[blo].equiv) {
 
504
        while (alo < ahi && blo < bhi && a[alo].equiv == b[blo].equiv)
 
505
            add_matching_line(answer, alo++, blo++);
 
506
        res = recurse_matches(answer, hashtable,
 
507
                              backpointers, a, b,
 
508
                              alo, blo, ahi, bhi, maxrecursion - 1);
 
509
        if (!res)
 
510
            goto error;
 
511
    }
 
512
 
 
513
    /* find matching lines at the very end */
 
514
    else if (a[ahi - 1].equiv == b[bhi - 1].equiv) {
 
515
        nahi = ahi - 1;
 
516
        nbhi = bhi - 1;
 
517
        while (nahi > alo && nbhi > blo && a[nahi - 1].equiv == b[nbhi - 1].equiv) {
 
518
            nahi--;
 
519
            nbhi--;
 
520
        }
 
521
        res = recurse_matches(answer, hashtable,
 
522
                              backpointers, a, b,
 
523
                              last_a_pos + 1, last_b_pos + 1,
 
524
                              nahi, nbhi, maxrecursion - 1);
 
525
        if (!res)
 
526
            goto error;
 
527
        for (i = 0; i < ahi - nahi; i++)
 
528
            add_matching_line(answer, nahi + i, nbhi + i);
 
529
    }
 
530
 
 
531
    return 1;
 
532
 
 
533
error:
 
534
    free(lcs);
 
535
    return 0;
 
536
}
 
537
 
 
538
 
 
539
static void
 
540
delete_lines(struct line *lines, Py_ssize_t size)
 
541
{
 
542
    struct line *line = lines;
 
543
    while (size-- > 0) {
 
544
        Py_XDECREF(line->data);
 
545
        line++;
 
546
    }
 
547
    free(lines);
 
548
}
 
549
 
 
550
 
 
551
static Py_ssize_t
 
552
load_lines(PyObject *orig, struct line **lines)
 
553
{
 
554
    Py_ssize_t size, i;
 
555
    struct line *line;
 
556
    PyObject *seq, *item;
 
557
 
 
558
    seq = PySequence_Fast(orig, "sequence expected");
 
559
    if (seq == NULL) {
 
560
        return -1;
 
561
    }
 
562
 
 
563
    size = PySequence_Fast_GET_SIZE(seq);
 
564
    if (size == 0) {
 
565
        Py_DECREF(seq);
 
566
        return 0;
 
567
    }
 
568
 
 
569
    /* Allocate a memory block for line data, initialized to 0 */
 
570
    line = *lines = (struct line *)calloc(size, sizeof(struct line));
 
571
    if (line == NULL) {
 
572
        PyErr_NoMemory();
 
573
        Py_DECREF(seq);
 
574
        return -1;
 
575
    }
 
576
 
 
577
    for (i = 0; i < size; i++) {
 
578
        item = PySequence_Fast_GET_ITEM(seq, i);
 
579
        Py_INCREF(item);
 
580
        line->data = item;
 
581
        line->hash = PyObject_Hash(item);
 
582
        if (line->hash == (-1)) {
 
583
            /* Propogate the hash exception */
 
584
            size = -1;
 
585
            goto cleanup;
 
586
        }
 
587
        line->next = SENTINEL;
 
588
        line++;
 
589
    }
 
590
 
 
591
    cleanup:
 
592
    Py_DECREF(seq);
 
593
    if (size == -1) {
 
594
        /* Error -- cleanup unused object references */
 
595
        delete_lines(*lines, i);
 
596
        *lines = NULL;
 
597
    }
 
598
    return size;
 
599
}
 
600
 
 
601
 
 
602
static PyObject *
 
603
py_unique_lcs(PyObject *self, PyObject *args)
 
604
{
 
605
    PyObject *aseq, *bseq, *res, *item;
 
606
    Py_ssize_t asize, bsize, i, nmatches, *backpointers = NULL;
 
607
    struct line *a = NULL, *b = NULL;
 
608
    struct matching_line *matches = NULL;
 
609
    struct hashtable hashtable;
 
610
 
 
611
    if (!PyArg_ParseTuple(args, "OO", &aseq, &bseq))
 
612
        return NULL;
 
613
 
 
614
    hashtable.table = NULL;
 
615
 
 
616
    asize = load_lines(aseq, &a);
 
617
    bsize = load_lines(bseq, &b);
 
618
    if (asize == -1 || bsize == -1)
 
619
        goto error;
 
620
 
 
621
    if (!equate_lines(&hashtable, a, b, asize, bsize))
 
622
        goto error;
 
623
 
 
624
    if (bsize > 0) {
 
625
        matches = (struct matching_line *)malloc(sizeof(struct matching_line) * bsize);
 
626
        if (matches == NULL)
 
627
            goto error;
 
628
 
 
629
        backpointers = (Py_ssize_t *)malloc(sizeof(Py_ssize_t) * bsize * 4);
 
630
        if (backpointers == NULL)
 
631
            goto error;
 
632
    }
 
633
 
 
634
    nmatches = unique_lcs(matches, &hashtable, backpointers, a, b, 0, 0, asize, bsize);
 
635
 
 
636
    res = PyList_New(nmatches);
 
637
    for (i = 0; i < nmatches; i++) {
 
638
        item = Py_BuildValue("nn", matches[nmatches - i - 1].a, matches[nmatches - i - 1].b);
 
639
        if (item == NULL)
 
640
            goto error;
 
641
        if (PyList_SetItem(res, i, item) != 0)
 
642
            goto error;
 
643
    }
 
644
 
 
645
    free(backpointers);
 
646
    free(matches);
 
647
    free(hashtable.table);
 
648
    delete_lines(b, bsize);
 
649
    delete_lines(a, asize);
 
650
    return res;
 
651
 
 
652
error:
 
653
    free(backpointers);
 
654
    free(matches);
 
655
    free(hashtable.table);
 
656
    delete_lines(b, bsize);
 
657
    delete_lines(a, asize);
 
658
    return NULL;
 
659
}
 
660
 
 
661
 
 
662
static PyObject *
 
663
py_recurse_matches(PyObject *self, PyObject *args)
 
664
{
 
665
    PyObject *aseq, *bseq, *item, *answer;
 
666
    int maxrecursion, res;
 
667
    Py_ssize_t i, j, asize, bsize, alo, blo, ahi, bhi;
 
668
    Py_ssize_t *backpointers = NULL;
 
669
    struct line *a = NULL, *b = NULL;
 
670
    struct hashtable hashtable;
 
671
    struct matching_blocks matches;
 
672
 
 
673
    if (!PyArg_ParseTuple(args, "OOnnnnOi", &aseq, &bseq, &alo, &blo,
 
674
                          &ahi, &bhi, &answer, &maxrecursion))
 
675
        return NULL;
 
676
 
 
677
    hashtable.table = NULL;
 
678
    matches.matches = NULL;
 
679
 
 
680
    asize = load_lines(aseq, &a);
 
681
    bsize = load_lines(bseq, &b);
 
682
    if (asize == -1 || bsize == -1)
 
683
        goto error;
 
684
 
 
685
    if (!equate_lines(&hashtable, a, b, asize, bsize))
 
686
        goto error;
 
687
 
 
688
    matches.count = 0;
 
689
 
 
690
    if (bsize > 0) {
 
691
        matches.matches = (struct matching_block *)malloc(sizeof(struct matching_block) * bsize);
 
692
        if (matches.matches == NULL)
 
693
            goto error;
 
694
 
 
695
        backpointers = (Py_ssize_t *)malloc(sizeof(Py_ssize_t) * bsize * 4);
 
696
        if (backpointers == NULL)
 
697
            goto error;
 
698
    } else {
 
699
        matches.matches = NULL;
 
700
        backpointers = NULL;
 
701
    }
 
702
 
 
703
    res = recurse_matches(&matches, &hashtable, backpointers,
 
704
                          a, b, alo, blo, ahi, bhi, maxrecursion);
 
705
    if (!res)
 
706
        goto error;
 
707
 
 
708
    for (i = 0; i < matches.count; i++) {
 
709
        for (j = 0; j < matches.matches[i].len; j++) {
 
710
            item = Py_BuildValue("nn", matches.matches[i].a + j,
 
711
                                 matches.matches[i].b + j);
 
712
            if (item == NULL)
 
713
                goto error;
 
714
            if (PyList_Append(answer, item) != 0)
 
715
                goto error;
 
716
        }
 
717
    }
 
718
 
 
719
    free(backpointers);
 
720
    free(matches.matches);
 
721
    free(hashtable.table);
 
722
    delete_lines(b, bsize);
 
723
    delete_lines(a, asize);
 
724
    Py_RETURN_NONE;
 
725
 
 
726
error:
 
727
    free(backpointers);
 
728
    free(matches.matches);
 
729
    free(hashtable.table);
 
730
    delete_lines(b, bsize);
 
731
    delete_lines(a, asize);
 
732
    return NULL;
 
733
}
 
734
 
 
735
 
 
736
static PyObject *
 
737
PatienceSequenceMatcher_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 
738
{
 
739
    PyObject *junk, *a, *b;
 
740
    PatienceSequenceMatcher *self;
 
741
 
 
742
    self = (PatienceSequenceMatcher *)type->tp_alloc(type, 0);
 
743
    if (self != NULL) {
 
744
 
 
745
        if (!PyArg_ParseTuple(args, "OOO", &junk, &a, &b)) {
 
746
            Py_DECREF(self);
 
747
            return NULL;
 
748
        }
 
749
 
 
750
        self->asize = load_lines(a, &(self->a));
 
751
        self->bsize = load_lines(b, &(self->b));
 
752
 
 
753
        if (self->asize == -1 || self->bsize == -1) {
 
754
            Py_DECREF(self);
 
755
            return NULL;
 
756
        }
 
757
 
 
758
        if (!equate_lines(&self->hashtable, self->a, self->b, self->asize, self->bsize)) {
 
759
            Py_DECREF(self);
 
760
            return NULL;
 
761
        }
 
762
 
 
763
        if (self->bsize > 0) {
 
764
            self->backpointers = (Py_ssize_t *)malloc(sizeof(Py_ssize_t) * self->bsize * 4);
 
765
            if (self->backpointers == NULL) {
 
766
                Py_DECREF(self);
 
767
                PyErr_NoMemory();
 
768
                return NULL;
 
769
            }
 
770
        } else {
 
771
            self->backpointers = NULL;
 
772
        }
 
773
 
 
774
    }
 
775
 
 
776
    return (PyObject *)self;
 
777
}
 
778
 
 
779
 
 
780
static void
 
781
PatienceSequenceMatcher_dealloc(PatienceSequenceMatcher* self)
 
782
{
 
783
    free(self->backpointers);
 
784
    free(self->hashtable.table);
 
785
    delete_lines(self->b, self->bsize);
 
786
    delete_lines(self->a, self->asize);
 
787
    ((PyObject *)self)->ob_type->tp_free((PyObject *)self);
 
788
}
 
789
 
 
790
 
 
791
static char PatienceSequenceMatcher_get_matching_blocks_doc[] =
 
792
    "Return list of triples describing matching subsequences.\n"
 
793
    "\n"
 
794
    "Each triple is of the form (i, j, n), and means that\n"
 
795
    "a[i:i+n] == b[j:j+n].  The triples are monotonically increasing in\n"
 
796
    "i and in j.\n"
 
797
    "\n"
 
798
    "The last triple is a dummy, (len(a), len(b), 0), and is the only\n"
 
799
    "triple with n==0.\n"
 
800
    "\n"
 
801
    ">>> s = PatienceSequenceMatcher(None, \"abxcd\", \"abcd\")\n"
 
802
    ">>> s.get_matching_blocks()\n"
 
803
    "[(0, 0, 2), (3, 2, 2), (5, 4, 0)]\n";
 
804
 
 
805
static PyObject *
 
806
PatienceSequenceMatcher_get_matching_blocks(PatienceSequenceMatcher* self)
 
807
{
 
808
    PyObject *answer, *item;
 
809
    int res;
 
810
    Py_ssize_t i;
 
811
    struct matching_blocks matches;
 
812
 
 
813
    matches.count = 0;
 
814
    if (self->bsize > 0) {
 
815
        matches.matches = (struct matching_block *)
 
816
            malloc(sizeof(struct matching_block) * self->bsize);
 
817
        if (matches.matches == NULL)
 
818
            return PyErr_NoMemory();
 
819
    } else
 
820
        matches.matches = NULL;
 
821
 
 
822
    res = recurse_matches(&matches, &self->hashtable, self->backpointers,
 
823
                          self->a, self->b, 0, 0,
 
824
                          self->asize, self->bsize, 10);
 
825
    if (!res) {
 
826
        free(matches.matches);
 
827
        return PyErr_NoMemory();
 
828
    }
 
829
 
 
830
    answer = PyList_New(matches.count + 1);
 
831
    if (answer == NULL) {
 
832
        free(matches.matches);
 
833
        return NULL;
 
834
    }
 
835
 
 
836
    for (i = 0; i < matches.count; i++) {
 
837
        item = Py_BuildValue("nnn", matches.matches[i].a,
 
838
                             matches.matches[i].b, matches.matches[i].len);
 
839
        if (item == NULL)
 
840
            goto error;
 
841
        if (PyList_SetItem(answer, i, item) != 0)
 
842
            goto error;
 
843
    }
 
844
    item = Py_BuildValue("nnn", self->asize, self->bsize, 0);
 
845
    if (item == NULL)
 
846
        goto error;
 
847
    if (PyList_SetItem(answer, i, item) != 0)
 
848
        goto error;
 
849
 
 
850
    free(matches.matches);
 
851
    return answer;
 
852
 
 
853
error:
 
854
    free(matches.matches);
 
855
    Py_DECREF(answer);
 
856
    return NULL;
 
857
}
 
858
 
 
859
 
 
860
static char PatienceSequenceMatcher_get_opcodes_doc[] =
 
861
    "Return list of 5-tuples describing how to turn a into b.\n"
 
862
    "\n"
 
863
    "Each tuple is of the form (tag, i1, i2, j1, j2).  The first tuple\n"
 
864
    "has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the\n"
 
865
    "tuple preceding it, and likewise for j1 == the previous j2.\n"
 
866
    "\n"
 
867
    "The tags are strings, with these meanings:\n"
 
868
    "\n"
 
869
    "'replace':  a[i1:i2] should be replaced by b[j1:j2]\n"
 
870
    "'delete':   a[i1:i2] should be deleted.\n"
 
871
    "               Note that j1==j2 in this case.\n"
 
872
    "'insert':   b[j1:j2] should be inserted at a[i1:i1].\n"
 
873
    "               Note that i1==i2 in this case.\n"
 
874
    "'equal':    a[i1:i2] == b[j1:j2]\n"
 
875
    "\n"
 
876
    ">>> a = \"qabxcd\"\n"
 
877
    ">>> b = \"abycdf\"\n"
 
878
    ">>> s = PatienceSequenceMatcher(None, a, b)\n"
 
879
    ">>> for tag, i1, i2, j1, j2 in s.get_opcodes():\n"
 
880
    "...    print (\"%7s a[%d:%d] (%s) b[%d:%d] (%s)\" %\n"
 
881
    "...           (tag, i1, i2, a[i1:i2], j1, j2, b[j1:j2]))\n"
 
882
    " delete a[0:1] (q) b[0:0] ()\n"
 
883
    "  equal a[1:3] (ab) b[0:2] (ab)\n"
 
884
    "replace a[3:4] (x) b[2:3] (y)\n"
 
885
    "  equal a[4:6] (cd) b[3:5] (cd)\n"
 
886
    " insert a[6:6] () b[5:6] (f)\n";
 
887
 
 
888
static PyObject *
 
889
PatienceSequenceMatcher_get_opcodes(PatienceSequenceMatcher* self)
 
890
{
 
891
    PyObject *answer, *item;
 
892
    Py_ssize_t i, j, k, ai, bj;
 
893
    int tag, res;
 
894
    struct matching_blocks matches;
 
895
 
 
896
    matches.count = 0;
 
897
    matches.matches = (struct matching_block *)malloc(sizeof(struct matching_block) * (self->bsize + 1));
 
898
    if (matches.matches == NULL)
 
899
        return PyErr_NoMemory();
 
900
 
 
901
    res = recurse_matches(&matches, &self->hashtable, self->backpointers,
 
902
                          self->a, self->b, 0, 0,
 
903
                          self->asize, self->bsize, 10);
 
904
    if (!res) {
 
905
        free(matches.matches);
 
906
        return PyErr_NoMemory();
 
907
    }
 
908
 
 
909
    matches.matches[matches.count].a = self->asize;
 
910
    matches.matches[matches.count].b = self->bsize;
 
911
    matches.matches[matches.count].len = 0;
 
912
    matches.count++;
 
913
 
 
914
    answer = PyList_New(0);
 
915
    if (answer == NULL) {
 
916
        free(matches.matches);
 
917
        return NULL;
 
918
    }
 
919
 
 
920
    i = j = 0;
 
921
    for (k = 0; k < matches.count; k++) {
 
922
        ai = matches.matches[k].a;
 
923
        bj = matches.matches[k].b;
 
924
 
 
925
        tag = -1;
 
926
        if (i < ai && j < bj)
 
927
            tag = OP_REPLACE;
 
928
        else if (i < ai)
 
929
            tag = OP_DELETE;
 
930
        else if (j < bj)
 
931
            tag = OP_INSERT;
 
932
 
 
933
        if (tag != -1) {
 
934
            item = Py_BuildValue("snnnn", opcode_names[tag], i, ai, j, bj);
 
935
            if (item == NULL)
 
936
                goto error;
 
937
            if (PyList_Append(answer, item) != 0)
 
938
                goto error;
 
939
        }
 
940
 
 
941
        i = ai + matches.matches[k].len;
 
942
        j = bj + matches.matches[k].len;
 
943
 
 
944
        if (matches.matches[k].len > 0) {
 
945
            item = Py_BuildValue("snnnn", opcode_names[OP_EQUAL], ai, i, bj, j);
 
946
            if (item == NULL)
 
947
                goto error;
 
948
            if (PyList_Append(answer, item) != 0)
 
949
                goto error;
 
950
        }
 
951
    }
 
952
 
 
953
    free(matches.matches);
 
954
    return answer;
 
955
 
 
956
error:
 
957
    free(matches.matches);
 
958
    Py_DECREF(answer);
 
959
    return NULL;
 
960
}
 
961
 
 
962
 
 
963
static char PatienceSequenceMatcher_get_grouped_opcodes_doc[] =
 
964
    "Isolate change clusters by eliminating ranges with no changes.\n"
 
965
    "\n"
 
966
    "Return a list of groups with upto n lines of context.\n"
 
967
    "Each group is in the same format as returned by get_opcodes().\n"
 
968
    "\n"
 
969
    ">>> from pprint import pprint\n"
 
970
    ">>> a = map(str, range(1,40))\n"
 
971
    ">>> b = a[:]\n"
 
972
    ">>> b[8:8] = ['i']     # Make an insertion\n"
 
973
    ">>> b[20] += 'x'       # Make a replacement\n"
 
974
    ">>> b[23:28] = []      # Make a deletion\n"
 
975
    ">>> b[30] += 'y'       # Make another replacement\n"
 
976
    ">>> pprint(PatienceSequenceMatcher(None,a,b).get_grouped_opcodes())\n"
 
977
    "[[('equal', 5, 8, 5, 8), ('insert', 8, 8, 8, 9), ('equal', 8, 11, 9, 12)],\n"
 
978
    " [('equal', 16, 19, 17, 20),\n"
 
979
    "  ('replace', 19, 20, 20, 21),\n"
 
980
    "  ('equal', 20, 22, 21, 23),\n"
 
981
    "  ('delete', 22, 27, 23, 23),\n"
 
982
    "  ('equal', 27, 30, 23, 26)],\n"
 
983
    " [('equal', 31, 34, 27, 30),\n"
 
984
    "  ('replace', 34, 35, 30, 31),\n"
 
985
    "  ('equal', 35, 38, 31, 34)]]\n";
 
986
 
 
987
static PyObject *
 
988
PatienceSequenceMatcher_get_grouped_opcodes(PatienceSequenceMatcher* self,
 
989
                                            PyObject *args)
 
990
{
 
991
    PyObject *answer, *group, *item;
 
992
    Py_ssize_t i, j, k, ai, bj, size, ncodes, tag;
 
993
    Py_ssize_t i1, i2, j1, j2;
 
994
    int n = 3, nn, res;
 
995
    struct matching_blocks matches;
 
996
    struct opcode *codes;
 
997
 
 
998
    if (!PyArg_ParseTuple(args, "|i", &n))
 
999
        return NULL;
 
1000
 
 
1001
    matches.count = 0;
 
1002
    matches.matches = (struct matching_block *)malloc(sizeof(struct matching_block) * (self->bsize + 1));
 
1003
    if (matches.matches == NULL)
 
1004
        return PyErr_NoMemory();
 
1005
 
 
1006
    res = recurse_matches(&matches, &self->hashtable, self->backpointers,
 
1007
                          self->a, self->b, 0, 0,
 
1008
                          self->asize, self->bsize, 10);
 
1009
    if (!res) {
 
1010
        free(matches.matches);
 
1011
        return PyErr_NoMemory();
 
1012
    }
 
1013
 
 
1014
    matches.matches[matches.count].a = self->asize;
 
1015
    matches.matches[matches.count].b = self->bsize;
 
1016
    matches.matches[matches.count].len = 0;
 
1017
    matches.count++;
 
1018
 
 
1019
    ncodes = 0;
 
1020
    codes = (struct opcode *)malloc(sizeof(struct opcode) * matches.count * 2);
 
1021
    if (codes == NULL) {
 
1022
        free(matches.matches);
 
1023
        return PyErr_NoMemory();
 
1024
    }
 
1025
 
 
1026
    i = j = 0;
 
1027
    for (k = 0; k < matches.count; k++) {
 
1028
        ai = matches.matches[k].a;
 
1029
        bj = matches.matches[k].b;
 
1030
 
 
1031
        tag = -1;
 
1032
        if (i < ai && j < bj)
 
1033
            tag = OP_REPLACE;
 
1034
        else if (i < ai)
 
1035
            tag = OP_DELETE;
 
1036
        else if (j < bj)
 
1037
            tag = OP_INSERT;
 
1038
 
 
1039
        if (tag != -1) {
 
1040
            codes[ncodes].tag = tag;
 
1041
            codes[ncodes].i1 = i;
 
1042
            codes[ncodes].i2 = ai;
 
1043
            codes[ncodes].j1 = j;
 
1044
            codes[ncodes].j2 = bj;
 
1045
            ncodes++;
 
1046
        }
 
1047
 
 
1048
        i = ai + matches.matches[k].len;
 
1049
        j = bj + matches.matches[k].len;
 
1050
 
 
1051
        if (matches.matches[k].len > 0) {
 
1052
            codes[ncodes].tag = OP_EQUAL;
 
1053
            codes[ncodes].i1 = ai;
 
1054
            codes[ncodes].i2 = i;
 
1055
            codes[ncodes].j1 = bj;
 
1056
            codes[ncodes].j2 = j;
 
1057
            ncodes++;
 
1058
        }
 
1059
    }
 
1060
 
 
1061
    if (ncodes == 0) {
 
1062
        codes[ncodes].tag = OP_EQUAL;
 
1063
        codes[ncodes].i1 = 0;
 
1064
        codes[ncodes].i2 = 1;
 
1065
        codes[ncodes].j1 = 0;
 
1066
        codes[ncodes].j2 = 1;
 
1067
        ncodes++;
 
1068
    }
 
1069
 
 
1070
    /* fixup leading and trailing groups if they show no changes. */
 
1071
    if (codes[0].tag == OP_EQUAL) {
 
1072
        codes[0].i1 = MAX(codes[0].i1, codes[0].i2 - n);
 
1073
        codes[0].j1 = MAX(codes[0].j1, codes[0].j2 - n);
 
1074
    }
 
1075
    if (codes[ncodes - 1].tag == OP_EQUAL) {
 
1076
        codes[ncodes - 1].i2 = MIN(codes[ncodes - 1].i2,
 
1077
                                   codes[ncodes - 1].i1 + n);
 
1078
        codes[ncodes - 1].j2 = MIN(codes[ncodes - 1].j2,
 
1079
                                   codes[ncodes - 1].j1 + n);
 
1080
    }
 
1081
 
 
1082
    group = NULL;
 
1083
 
 
1084
    answer = PyList_New(0);
 
1085
    if (answer == NULL)
 
1086
        goto error;
 
1087
 
 
1088
    group = PyList_New(0);
 
1089
    if (group == NULL)
 
1090
        goto error;
 
1091
 
 
1092
    nn = n + n;
 
1093
    tag = -1;
 
1094
    for (i = 0; i < ncodes; i++) {
 
1095
        tag = codes[i].tag;
 
1096
        i1 = codes[i].i1;
 
1097
        i2 = codes[i].i2;
 
1098
        j1 = codes[i].j1;
 
1099
        j2 = codes[i].j2;
 
1100
        /* end the current group and start a new one whenever
 
1101
           there is a large range with no changes. */
 
1102
        if (tag == OP_EQUAL && i2 - i1 > nn) {
 
1103
            item = Py_BuildValue("snnnn", opcode_names[tag],
 
1104
                                  i1, MIN(i2, i1 + n), j1, MIN(j2, j1 + n));
 
1105
            if (item == NULL)
 
1106
                goto error;
 
1107
            if (PyList_Append(group, item) != 0)
 
1108
                goto error;
 
1109
            if (PyList_Append(answer, group) != 0)
 
1110
                goto error;
 
1111
            group = PyList_New(0);
 
1112
            if (group == NULL)
 
1113
                goto error;
 
1114
            i1 = MAX(i1, i2 - n);
 
1115
            j1 = MAX(j1, j2 - n);
 
1116
        }
 
1117
        item = Py_BuildValue("snnnn", opcode_names[tag], i1, i2, j1 ,j2);
 
1118
        if (item == NULL)
 
1119
            goto error;
 
1120
        if (PyList_Append(group, item) != 0)
 
1121
            goto error;
 
1122
    }
 
1123
    size = PyList_Size(group);
 
1124
    if (size > 0 && !(size == 1 && tag == OP_EQUAL)) {
 
1125
        if (PyList_Append(answer, group) != 0)
 
1126
            goto error;
 
1127
    }
 
1128
    else
 
1129
        Py_DECREF(group);
 
1130
 
 
1131
    free(codes);
 
1132
    free(matches.matches);
 
1133
    return answer;
 
1134
 
 
1135
error:
 
1136
    free(codes);
 
1137
    free(matches.matches);
 
1138
    Py_DECREF(group);
 
1139
    Py_DECREF(answer);
 
1140
    return NULL;
 
1141
}
 
1142
 
 
1143
 
 
1144
static PyMethodDef PatienceSequenceMatcher_methods[] = {
 
1145
    {"get_matching_blocks",
 
1146
     (PyCFunction)PatienceSequenceMatcher_get_matching_blocks,
 
1147
     METH_NOARGS,
 
1148
     PatienceSequenceMatcher_get_matching_blocks_doc},
 
1149
    {"get_opcodes",
 
1150
     (PyCFunction)PatienceSequenceMatcher_get_opcodes,
 
1151
     METH_NOARGS,
 
1152
     PatienceSequenceMatcher_get_opcodes_doc},
 
1153
    {"get_grouped_opcodes",
 
1154
     (PyCFunction)PatienceSequenceMatcher_get_grouped_opcodes,
 
1155
     METH_VARARGS,
 
1156
     PatienceSequenceMatcher_get_grouped_opcodes_doc},
 
1157
    {NULL}
 
1158
};
 
1159
 
 
1160
 
 
1161
static char PatienceSequenceMatcher_doc[] =
 
1162
    "C implementation of PatienceSequenceMatcher";
 
1163
 
 
1164
 
 
1165
static PyTypeObject PatienceSequenceMatcherType = {
 
1166
    PyObject_HEAD_INIT(NULL)
 
1167
    .tp_name = "PatienceSequenceMatcher",
 
1168
    .tp_basicsize = sizeof(PatienceSequenceMatcher),
 
1169
    .tp_dealloc = (destructor)PatienceSequenceMatcher_dealloc,
 
1170
    .tp_flags = Py_TPFLAGS_DEFAULT,
 
1171
    .tp_doc = PatienceSequenceMatcher_doc,
 
1172
    .tp_methods = PatienceSequenceMatcher_methods,
 
1173
    .tp_new = PatienceSequenceMatcher_new,
 
1174
};
 
1175
 
 
1176
 
 
1177
static PyMethodDef cpatiencediff_methods[] = {
 
1178
    {"unique_lcs_c", py_unique_lcs, METH_VARARGS},
 
1179
    {"recurse_matches_c", py_recurse_matches, METH_VARARGS},
 
1180
    {NULL, NULL}
 
1181
};
 
1182
 
 
1183
PYMOD_INIT_FUNC(_patiencediff_c)
 
1184
{
 
1185
    PyObject* m;
 
1186
 
 
1187
    if (PyType_Ready(&PatienceSequenceMatcherType) < 0)
 
1188
        return PYMOD_ERROR;
 
1189
 
 
1190
    PYMOD_CREATE(m, "_patiencediff_c",
 
1191
                 "C implementation of PatienceSequenceMatcher",
 
1192
                 cpatiencediff_methods);
 
1193
    if (m == NULL) {
 
1194
        return PYMOD_ERROR;
 
1195
    }
 
1196
    Py_INCREF(&PatienceSequenceMatcherType);
 
1197
    PyModule_AddObject(m, "PatienceSequenceMatcher_c",
 
1198
                       (PyObject *)&PatienceSequenceMatcherType);
 
1199
    return PYMOD_SUCCESS(m);
 
1200
}
 
1201
 
 
1202
 
 
1203
/* vim: sw=4 et 
 
1204
 */