~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/_patiencediff_c.c

  • Committer: Andrew Bennetts
  • Date: 2008-09-05 10:48:03 UTC
  • mto: This revision was merged to the branch mainline in revision 3693.
  • Revision ID: andrew.bennetts@canonical.com-20080905104803-6g72dz6wcldosfs2
Remove monkey-patching of branch._ensure_real from test_remote.py.

Show diffs side-by-side

added added

removed removed

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