~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/_patiencediff_c.c

  • Committer: Robert Collins
  • Date: 2006-07-20 13:00:31 UTC
  • mto: (1852.9.1 Tree.compare().)
  • mto: This revision was merged to the branch mainline in revision 1890.
  • Revision ID: robertc@robertcollins.net-20060720130031-d26103a427ea10f3
StartĀ treeĀ implementationĀ tests.

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