~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/_patiencediff_c.c

  • Committer: mbp at sourcefrog
  • Date: 2005-04-11 02:53:57 UTC
  • Revision ID: mbp@sourcefrog.net-20050411025357-af577721308648ae
- remove profiler temporary file when done

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
 
    int hash;          /* hash code of the string */
74
 
    Py_ssize_t next;   /* next line from the same equivalence class */
75
 
    Py_ssize_t equiv;  /* equivalence class */
76
 
    Py_ssize_t len;
77
 
    const char *data;
78
 
};
79
 
 
80
 
 
81
 
struct bucket {
82
 
    Py_ssize_t a_head;  /* first item in `a` from this equivalence class */
83
 
    Py_ssize_t a_count;
84
 
    Py_ssize_t b_head;  /* first item in `b` from this equivalence class */
85
 
    Py_ssize_t b_count;
86
 
    Py_ssize_t a_pos;
87
 
    Py_ssize_t b_pos;
88
 
};
89
 
 
90
 
 
91
 
struct hashtable {
92
 
    Py_ssize_t last_a_pos;
93
 
    Py_ssize_t last_b_pos;
94
 
    Py_ssize_t size;
95
 
    struct bucket *table;
96
 
};
97
 
 
98
 
struct matching_line {
99
 
    Py_ssize_t a;     /* index of the line in `a` */
100
 
    Py_ssize_t b;     /* index of the line in `b` */
101
 
};
102
 
 
103
 
 
104
 
struct matching_block {
105
 
    Py_ssize_t a;     /* index of the first line in `a` */
106
 
    Py_ssize_t b;     /* index of the first line in `b` */
107
 
    Py_ssize_t len;   /* length of the block */
108
 
};
109
 
 
110
 
 
111
 
struct matching_blocks {
112
 
    struct matching_block *matches;
113
 
    Py_ssize_t count;
114
 
};
115
 
 
116
 
 
117
 
struct opcode {
118
 
    int tag;
119
 
    Py_ssize_t i1;
120
 
    Py_ssize_t i2;
121
 
    Py_ssize_t j1;
122
 
    Py_ssize_t j2;
123
 
};
124
 
 
125
 
 
126
 
typedef struct {
127
 
    PyObject_HEAD
128
 
    Py_ssize_t asize;
129
 
    Py_ssize_t bsize;
130
 
    struct line *a;
131
 
    struct line *b;
132
 
    struct hashtable hashtable;
133
 
    Py_ssize_t *backpointers;
134
 
} PatienceSequenceMatcher;
135
 
 
136
 
 
137
 
static inline Py_ssize_t
138
 
bisect_left(Py_ssize_t *list, Py_ssize_t item, Py_ssize_t lo, Py_ssize_t hi)
139
 
{
140
 
    while (lo < hi) {
141
 
        Py_ssize_t mid = lo / 2 + hi / 2 + (lo % 2 + hi % 2) / 2;
142
 
        if (list[mid] < item)
143
 
            lo = mid + 1;
144
 
        else
145
 
            hi = mid;
146
 
    }
147
 
    return lo;
148
 
}
149
 
 
150
 
 
151
 
static inline int
152
 
compare_lines(struct line *a, struct line *b)
153
 
{
154
 
    return ((a->hash != b->hash) || (a->len != b->len) ||
155
 
            memcmp(a->data, b->data, a->len));
156
 
}
157
 
 
158
 
 
159
 
static inline int
160
 
find_equivalence_class(struct bucket *hashtable, Py_ssize_t hsize,
161
 
                       struct line *lines, struct line *ref_lines,
162
 
                       Py_ssize_t i)
163
 
{
164
 
    Py_ssize_t j;
165
 
    for (j = lines[i].hash & hsize; hashtable[j].b_head != SENTINEL; j = (j + 1) & hsize) {
166
 
        if (!compare_lines(lines + i, ref_lines + hashtable[j].b_head)) {
167
 
            break;
168
 
        }
169
 
    }
170
 
    return j;
171
 
}
172
 
 
173
 
 
174
 
static int
175
 
equate_lines(struct hashtable *result,
176
 
             struct line *lines_a, struct line *lines_b,
177
 
             Py_ssize_t asize, Py_ssize_t bsize)
178
 
{
179
 
    Py_ssize_t i, j, hsize;
180
 
    struct bucket *hashtable;
181
 
 
182
 
    /* check for overflow, we need the table to be at least bsize+1 */
183
 
    if (bsize == PY_SSIZE_T_MAX) {
184
 
        PyErr_SetNone(PyExc_OverflowError);
185
 
        return 0;
186
 
    }
187
 
 
188
 
    /* build a hash table of the next highest power of 2 */
189
 
    hsize = 1;
190
 
    while (hsize < bsize + 1)
191
 
        hsize *= 2;
192
 
 
193
 
    hashtable = (struct bucket *)malloc(sizeof(struct bucket) * hsize);
194
 
    if (hashtable == NULL) {
195
 
        PyErr_NoMemory();
196
 
        return 0;
197
 
    }
198
 
 
199
 
    /* initialise the hashtable */
200
 
    for (i = 0; i < hsize; i++) {
201
 
        hashtable[i].a_count = 0;
202
 
        hashtable[i].b_count = 0;
203
 
        hashtable[i].a_head = SENTINEL;
204
 
        hashtable[i].b_head = SENTINEL;
205
 
    }
206
 
    hsize--;
207
 
 
208
 
    /* add lines from lines_b to the hash table chains. iterating
209
 
       backwards so the matching lines are sorted to the linked list
210
 
       by the line number (because we are adding new lines to the
211
 
       head of the list) */
212
 
    for (i = bsize - 1; i >= 0; i--) {
213
 
        /* find the first hashtable entry, which is either empty or contains
214
 
           the same line as lines_b[i] */
215
 
        j = find_equivalence_class(hashtable, hsize, lines_b, lines_b, i);
216
 
 
217
 
        /* set the equivalence class */
218
 
        lines_b[i].equiv = j;
219
 
 
220
 
        /* add to the head of the equivalence class */
221
 
        lines_b[i].next = hashtable[j].b_head;
222
 
        hashtable[j].b_head = i;
223
 
        hashtable[j].b_count++;
224
 
    }
225
 
 
226
 
    /* match items from lines_a to their equivalence class in lines_b.
227
 
       again, iterating backwards for the right order of the linked lists */
228
 
    for (i = asize - 1; i >= 0; i--) {
229
 
        /* find the first hash entry, which is either empty or contains
230
 
           the same line as lines_a[i] */
231
 
        j = find_equivalence_class(hashtable, hsize, lines_a, lines_b, i);
232
 
 
233
 
        /* set the equivalence class, even if we are not interested in this
234
 
           line, because the values are not pre-filled */
235
 
        lines_a[i].equiv = j;
236
 
 
237
 
        /* we are not interested in lines which are not also in lines_b */
238
 
        if (hashtable[j].b_head == SENTINEL)
239
 
            continue;
240
 
 
241
 
        /* add to the head of the equivalence class */
242
 
        lines_a[i].next = hashtable[j].a_head;
243
 
        hashtable[j].a_head = i;
244
 
        hashtable[j].a_count++;
245
 
    }
246
 
 
247
 
    result->last_a_pos = -1;
248
 
    result->last_b_pos = -1;
249
 
    result->size = hsize + 1;
250
 
    result->table = hashtable;
251
 
 
252
 
    return 1;
253
 
}
254
 
 
255
 
 
256
 
 
257
 
/* Finds longest common subsequence of unique lines in a[alo:ahi] and
258
 
   b[blo:bhi].
259
 
   Parameter backpointers must have allocated memory for at least
260
 
   4 * (bhi - blo) ints. */
261
 
Py_ssize_t
262
 
unique_lcs(struct matching_line *answer,
263
 
           struct hashtable *hashtable, Py_ssize_t *backpointers,
264
 
           struct line *lines_a, struct line *lines_b,
265
 
           Py_ssize_t alo, Py_ssize_t blo, Py_ssize_t ahi, Py_ssize_t bhi)
266
 
{
267
 
    Py_ssize_t i, k, equiv, apos, bpos, norm_apos, norm_bpos, bsize, stacksize;
268
 
    Py_ssize_t *stacks, *lasts, *btoa;
269
 
    struct bucket *h;
270
 
 
271
 
    k = 0;
272
 
    stacksize = 0;
273
 
    bsize = bhi - blo;
274
 
    h = hashtable->table;
275
 
 
276
 
    /* "unpack" the allocated memory */
277
 
    stacks = backpointers + bsize;
278
 
    lasts = stacks + bsize;
279
 
    btoa = lasts + bsize;
280
 
 
281
 
    /* initialise the backpointers */
282
 
    for (i = 0; i < bsize; i++)
283
 
        backpointers[i] = SENTINEL;
284
 
 
285
 
    if (hashtable->last_a_pos == -1 || hashtable->last_a_pos > alo)
286
 
        for (i = 0; i < hashtable->size; i++)
287
 
            h[i].a_pos = h[i].a_head;
288
 
    hashtable->last_a_pos = alo;
289
 
 
290
 
    if (hashtable->last_b_pos == -1 || hashtable->last_b_pos > blo)
291
 
        for (i = 0; i < hashtable->size; i++)
292
 
            h[i].b_pos = h[i].b_head;
293
 
    hashtable->last_b_pos = blo;
294
 
 
295
 
    for (bpos = blo; bpos < bhi; bpos++) {
296
 
        equiv = lines_b[bpos].equiv;
297
 
 
298
 
        /* no lines in a or b  */
299
 
        if (h[equiv].a_count == 0 || h[equiv].b_count == 0)
300
 
            continue;
301
 
 
302
 
        /* find an unique line in lines_a that matches lines_b[bpos]
303
 
           if we find more than one line within the range alo:ahi,
304
 
           jump to the next line from lines_b immediately */
305
 
        apos = SENTINEL;
306
 
        /* loop through all lines in the linked list */
307
 
        for (i = h[equiv].a_pos; i != SENTINEL; i = lines_a[i].next) {
308
 
            /* the index is lower than alo, the the next line */
309
 
            if (i < alo) {
310
 
                h[equiv].a_pos = i;
311
 
                continue;
312
 
            }
313
 
            /* the index is higher than ahi, stop searching */
314
 
            if (i >= ahi)
315
 
                break;
316
 
            /* if the line is within our range, check if it's a duplicate */
317
 
            if (apos != SENTINEL)
318
 
                goto nextb;
319
 
            /* save index to the line */
320
 
            apos = i;
321
 
        }
322
 
        /* this line has no equivalent in lines_a[alo:ahi] */
323
 
        if (apos == SENTINEL)
324
 
            goto nextb;
325
 
 
326
 
        /* check for duplicates of this line in lines_b[blo:bhi] */
327
 
        /* loop through all lines in the linked list */
328
 
        for (i = h[equiv].b_pos; i != SENTINEL; i = lines_b[i].next) {
329
 
            /* the index is lower than blo, the the next line */
330
 
            if (i < blo) {
331
 
                h[equiv].b_pos = i;
332
 
                continue;
333
 
            }
334
 
            /* the index is higher than bhi, stop searching */
335
 
            if (i >= bhi)
336
 
                break;
337
 
            /* if this isn't the line with started with and it's within
338
 
               our range, it's a duplicate */
339
 
            if (i != bpos)
340
 
                goto nextb;
341
 
        }
342
 
 
343
 
        /* use normalised indexes ([0,ahi-alo) instead of [alo,ahi))
344
 
           for the patience sorting algorithm */
345
 
        norm_bpos = bpos - blo;
346
 
        norm_apos = apos - alo;
347
 
        btoa[norm_bpos] = norm_apos;
348
 
 
349
 
        /*
350
 
        Ok, how does this work...
351
 
 
352
 
        We have a list of matching lines from two lists, a and b. These
353
 
        matches are stored in variable `btoa`. As we are iterating over this
354
 
        table by bpos, the lines from b already form an increasing sequence.
355
 
        We need to "sort" also the lines from a using the patience sorting
356
 
        algorithm, ignoring the lines which would need to be swapped.
357
 
 
358
 
          http://en.wikipedia.org/wiki/Patience_sorting
359
 
 
360
 
        For each pair of lines, we need to place the line from a on either
361
 
        an existing pile that has higher value on the top or create a new
362
 
        pile. Variable `stacks` represents the tops of these piles and in
363
 
        variable `lasts` we store the lines from b, that correspond to the
364
 
        lines from a in `stacks`.
365
 
 
366
 
        Whenever we place a new line on top of a pile, we store a
367
 
        backpointer to the line (b) from top of the previous pile. This means
368
 
        that after the loop, variable `backpointers` will contain an index
369
 
        to the previous matching lines that forms an increasing sequence
370
 
        (over both indexes a and b) with the current matching lines. If
371
 
        either index a or b of the previous matching lines would be higher
372
 
        than indexes of the current one or if the indexes of the current
373
 
        one are 0, it will contain SENTINEL.
374
 
 
375
 
        To construct the LCS, we will just need to follow these backpointers
376
 
        from the top of the last pile and stop when we reach SENTINEL.
377
 
        */
378
 
 
379
 
        /* as an optimization, check if the next line comes at the end,
380
 
           because it usually does */
381
 
        if (stacksize && stacks[stacksize - 1] < norm_apos)
382
 
            k = stacksize;
383
 
        /* as an optimization, check if the next line comes right after
384
 
           the previous line, because usually it does */
385
 
        else if (stacksize && (stacks[k] < norm_apos) &&
386
 
                 (k == stacksize - 1 || stacks[k + 1] > norm_apos))
387
 
            k += 1;
388
 
        else
389
 
            k = bisect_left(stacks, norm_apos, 0, stacksize);
390
 
 
391
 
        if (k > 0)
392
 
            backpointers[norm_bpos] = lasts[k - 1];
393
 
 
394
 
        if (k < stacksize) {
395
 
            stacks[k] = norm_apos;
396
 
            lasts[k] = norm_bpos;
397
 
        }
398
 
        else {
399
 
            stacks[stacksize] = norm_apos;
400
 
            lasts[stacksize] = norm_bpos;
401
 
            stacksize += 1;
402
 
        }
403
 
 
404
 
 
405
 
nextb:
406
 
        ;
407
 
    }
408
 
 
409
 
    if (stacksize == 0)
410
 
        return 0;
411
 
 
412
 
    /* backtrace the structures to find the LCS */
413
 
    i = 0;
414
 
    k = lasts[stacksize - 1];
415
 
    while (k != SENTINEL) {
416
 
        answer[i].a = btoa[k];
417
 
        answer[i].b = k;
418
 
        k = backpointers[k];
419
 
        i++;
420
 
    }
421
 
 
422
 
    return i;
423
 
}
424
 
 
425
 
/* Adds a new line to the list of matching blocks, either extending the
426
 
   current block or adding a new one. */
427
 
static inline void
428
 
add_matching_line(struct matching_blocks *answer, Py_ssize_t a, Py_ssize_t b)
429
 
{
430
 
    Py_ssize_t last_index = answer->count - 1;
431
 
    if ((last_index >= 0) &&
432
 
        (a == answer->matches[last_index].a +
433
 
              answer->matches[last_index].len) &&
434
 
        (b == answer->matches[last_index].b +
435
 
              answer->matches[last_index].len)) {
436
 
        /* enlarge the last block */
437
 
        answer->matches[last_index].len++;
438
 
    }
439
 
    else {
440
 
        /* create a new block */
441
 
        last_index++;
442
 
        answer->matches[last_index].a = a;
443
 
        answer->matches[last_index].b = b;
444
 
        answer->matches[last_index].len = 1;
445
 
        answer->count++;
446
 
    }
447
 
}
448
 
 
449
 
 
450
 
static int
451
 
recurse_matches(struct matching_blocks *answer, struct hashtable *hashtable,
452
 
                Py_ssize_t *backpointers, struct line *a, struct line *b,
453
 
                Py_ssize_t alo, Py_ssize_t blo, Py_ssize_t ahi, Py_ssize_t bhi,
454
 
                int maxrecursion)
455
 
{
456
 
    int res;
457
 
    Py_ssize_t new, last_a_pos, last_b_pos, lcs_size, nahi, nbhi, i, apos, bpos;
458
 
    struct matching_line *lcs;
459
 
 
460
 
    if (maxrecursion < 0)
461
 
        return 1;
462
 
 
463
 
    if (alo == ahi || blo == bhi)
464
 
        return 1;
465
 
 
466
 
    new = 0;
467
 
    last_a_pos = alo - 1;
468
 
    last_b_pos = blo - 1;
469
 
 
470
 
    lcs = (struct matching_line *)malloc(sizeof(struct matching_line) * (bhi - blo));
471
 
    if (lcs == NULL)
472
 
        return 0;
473
 
 
474
 
    lcs_size = unique_lcs(lcs, hashtable, backpointers, a, b, alo, blo, ahi, bhi);
475
 
 
476
 
    /* recurse between lines which are unique in each file and match */
477
 
    for (i = lcs_size - 1; i >= 0; i--) {
478
 
        apos = alo + lcs[i].a;
479
 
        bpos = blo + lcs[i].b;
480
 
        if (last_a_pos + 1 != apos || last_b_pos + 1 != bpos) {
481
 
            res = recurse_matches(answer, hashtable,
482
 
                                  backpointers, a, b,
483
 
                                  last_a_pos + 1, last_b_pos + 1,
484
 
                                  apos, bpos, maxrecursion - 1);
485
 
            if (!res)
486
 
                goto error;
487
 
        }
488
 
        last_a_pos = apos;
489
 
        last_b_pos = bpos;
490
 
        add_matching_line(answer, apos, bpos);
491
 
        new = 1;
492
 
    }
493
 
 
494
 
    free(lcs);
495
 
    lcs = NULL;
496
 
 
497
 
    /* find matches between the last match and the end */
498
 
    if (new > 0) {
499
 
        res = recurse_matches(answer, hashtable,
500
 
                              backpointers, a, b,
501
 
                              last_a_pos + 1, last_b_pos + 1,
502
 
                              ahi, bhi, maxrecursion - 1);
503
 
        if (!res)
504
 
            goto error;
505
 
    }
506
 
 
507
 
 
508
 
    /* find matching lines at the very beginning */
509
 
    else if (a[alo].equiv == b[blo].equiv) {
510
 
        while (alo < ahi && blo < bhi && a[alo].equiv == b[blo].equiv)
511
 
            add_matching_line(answer, alo++, blo++);
512
 
        res = recurse_matches(answer, hashtable,
513
 
                              backpointers, a, b,
514
 
                              alo, blo, ahi, bhi, maxrecursion - 1);
515
 
        if (!res)
516
 
            goto error;
517
 
    }
518
 
 
519
 
    /* find matching lines at the very end */
520
 
    else if (a[ahi - 1].equiv == b[bhi - 1].equiv) {
521
 
        nahi = ahi - 1;
522
 
        nbhi = bhi - 1;
523
 
        while (nahi > alo && nbhi > blo && a[nahi - 1].equiv == b[nbhi - 1].equiv) {
524
 
            nahi--;
525
 
            nbhi--;
526
 
        }
527
 
        res = recurse_matches(answer, hashtable,
528
 
                              backpointers, a, b,
529
 
                              last_a_pos + 1, last_b_pos + 1,
530
 
                              nahi, nbhi, maxrecursion - 1);
531
 
        if (!res)
532
 
            goto error;
533
 
        for (i = 0; i < ahi - nahi; i++)
534
 
            add_matching_line(answer, nahi + i, nbhi + i);
535
 
    }
536
 
 
537
 
    return 1;
538
 
 
539
 
error:
540
 
    free(lcs);
541
 
    return 0;
542
 
}
543
 
 
544
 
 
545
 
static Py_ssize_t
546
 
load_lines(PyObject *orig, struct line **lines)
547
 
{
548
 
    Py_ssize_t size, i, j;
549
 
    int h;
550
 
    char *p;
551
 
    struct line *line;
552
 
    PyObject *seq, *item;
553
 
 
554
 
    seq = PySequence_Fast(orig, "sequence expected");
555
 
    if (seq == NULL) {
556
 
        return -1;
557
 
    }
558
 
 
559
 
    size = PySequence_Fast_GET_SIZE(seq);
560
 
    if (size == 0) {
561
 
        Py_DECREF(seq);
562
 
        return 0;
563
 
    }
564
 
 
565
 
    line = *lines = (struct line *)malloc(sizeof(struct line) * size);
566
 
    if (line == NULL) {
567
 
        PyErr_NoMemory();
568
 
        Py_DECREF(seq);
569
 
        return -1;
570
 
    }
571
 
 
572
 
    for (i = 0; i < size; i++) {
573
 
        item = PySequence_Fast_GET_ITEM(seq, i);
574
 
        if (!PyString_Check(item)){
575
 
            PyErr_Format(PyExc_TypeError,
576
 
                     "sequence item %zd: expected string,"
577
 
                     " %.80s found",
578
 
                     i, item->ob_type->tp_name);
579
 
            Py_DECREF(seq);
580
 
            return -1;
581
 
        }
582
 
        line->len = PyString_GET_SIZE(item);
583
 
        line->data = p = PyString_AS_STRING(item);
584
 
        /* 'djb2' hash. This gives us a nice compromise between fast hash
585
 
            function and a hash with less collisions. The algorithm doesn't
586
 
            use the hash for actual lookups, only for building the table
587
 
            so a better hash function wouldn't bring us much benefit, but
588
 
            would make this loading code slower. */
589
 
        h = 5381;
590
 
        for (j = 0; j < line->len; j++)
591
 
            h = ((h << 5) + h) + *p++;
592
 
        line->hash = h;
593
 
        line->next = SENTINEL;
594
 
        line++;
595
 
    }
596
 
 
597
 
    Py_DECREF(seq);
598
 
    return size;
599
 
}
600
 
 
601
 
 
602
 
static PyObject *
603
 
py_unique_lcs(PyObject *self, PyObject *args)
604
 
{
605
 
    PyObject *aseq, *bseq, *res, *item;
606
 
    Py_ssize_t asize, bsize, i, nmatches, *backpointers = NULL;
607
 
    struct line *a = NULL, *b = NULL;
608
 
    struct matching_line *matches = NULL;
609
 
    struct hashtable hashtable;
610
 
 
611
 
    if (!PyArg_ParseTuple(args, "OO", &aseq, &bseq))
612
 
        return NULL;
613
 
 
614
 
    hashtable.table = NULL;
615
 
 
616
 
    asize = load_lines(aseq, &a);
617
 
    bsize = load_lines(bseq, &b);
618
 
    if (asize == -1 || bsize == -1)
619
 
        goto error;
620
 
 
621
 
    if (!equate_lines(&hashtable, a, b, asize, bsize))
622
 
        goto error;
623
 
 
624
 
    matches = (struct matching_line *)malloc(sizeof(struct matching_line) * bsize);
625
 
    if (matches == NULL)
626
 
        goto error;
627
 
 
628
 
    backpointers = (Py_ssize_t *)malloc(sizeof(Py_ssize_t) * bsize * 4);
629
 
    if (backpointers == NULL)
630
 
        goto error;
631
 
 
632
 
    nmatches = unique_lcs(matches, &hashtable, backpointers, a, b, 0, 0, asize, bsize);
633
 
 
634
 
    res = PyList_New(nmatches);
635
 
    for (i = 0; i < nmatches; i++) {
636
 
#if PY_VERSION_HEX < 0x02050000
637
 
        item = Py_BuildValue("ii", matches[nmatches - i - 1].a, matches[nmatches - i - 1].b);
638
 
#else
639
 
        item = Py_BuildValue("nn", matches[nmatches - i - 1].a, matches[nmatches - i - 1].b);
640
 
#endif
641
 
        if (item == NULL)
642
 
            goto error;
643
 
        if (PyList_SetItem(res, i, item) != 0)
644
 
            goto error;
645
 
    }
646
 
 
647
 
    free(backpointers);
648
 
    free(matches);
649
 
    free(hashtable.table);
650
 
    free(b);
651
 
    free(a);
652
 
    return res;
653
 
 
654
 
error:
655
 
    free(backpointers);
656
 
    free(matches);
657
 
    free(hashtable.table);
658
 
    free(b);
659
 
    free(a);
660
 
    return NULL;
661
 
}
662
 
 
663
 
 
664
 
static PyObject *
665
 
py_recurse_matches(PyObject *self, PyObject *args)
666
 
{
667
 
    PyObject *aseq, *bseq, *item, *answer;
668
 
    int maxrecursion, res;
669
 
    Py_ssize_t i, j, asize, bsize, alo, blo, ahi, bhi;
670
 
    Py_ssize_t *backpointers = NULL;
671
 
    struct line *a = NULL, *b = NULL;
672
 
    struct hashtable hashtable;
673
 
    struct matching_blocks matches;
674
 
 
675
 
#if PY_VERSION_HEX < 0x02050000
676
 
    if (!PyArg_ParseTuple(args, "OOiiiiOi", &aseq, &bseq, &alo, &blo,
677
 
                          &ahi, &bhi, &answer, &maxrecursion))
678
 
#else
679
 
    if (!PyArg_ParseTuple(args, "OOnnnnOi", &aseq, &bseq, &alo, &blo,
680
 
                          &ahi, &bhi, &answer, &maxrecursion))
681
 
#endif
682
 
        return NULL;
683
 
 
684
 
    hashtable.table = NULL;
685
 
    matches.matches = NULL;
686
 
 
687
 
    asize = load_lines(aseq, &a);
688
 
    bsize = load_lines(bseq, &b);
689
 
    if (asize == -1 || bsize == -1)
690
 
        goto error;
691
 
 
692
 
    if (!equate_lines(&hashtable, a, b, asize, bsize))
693
 
        goto error;
694
 
 
695
 
    matches.count = 0;
696
 
    matches.matches = (struct matching_block *)malloc(sizeof(struct matching_block) * bsize);
697
 
    if (matches.matches == NULL)
698
 
        goto error;
699
 
 
700
 
    backpointers = (Py_ssize_t *)malloc(sizeof(Py_ssize_t) * bsize * 4);
701
 
    if (backpointers == NULL)
702
 
        goto error;
703
 
 
704
 
    res = recurse_matches(&matches, &hashtable, backpointers,
705
 
                          a, b, alo, blo, ahi, bhi, maxrecursion);
706
 
    if (!res)
707
 
        goto error;
708
 
 
709
 
    for (i = 0; i < matches.count; i++) {
710
 
        for (j = 0; j < matches.matches[i].len; j++) {
711
 
#if PY_VERSION_HEX < 0x02050000
712
 
            item = Py_BuildValue("ii", matches.matches[i].a + j,
713
 
                                 matches.matches[i].b + j);
714
 
#else
715
 
            item = Py_BuildValue("nn", matches.matches[i].a + j,
716
 
                                 matches.matches[i].b + j);
717
 
#endif
718
 
            if (item == NULL)
719
 
                goto error;
720
 
            if (PyList_Append(answer, item) != 0)
721
 
                goto error;
722
 
        }
723
 
    }
724
 
 
725
 
    free(backpointers);
726
 
    free(matches.matches);
727
 
    free(hashtable.table);
728
 
    free(b);
729
 
    free(a);
730
 
    Py_RETURN_NONE;
731
 
 
732
 
error:
733
 
    free(backpointers);
734
 
    free(matches.matches);
735
 
    free(hashtable.table);
736
 
    free(b);
737
 
    free(a);
738
 
    return NULL;
739
 
}
740
 
 
741
 
 
742
 
static PyObject *
743
 
PatienceSequenceMatcher_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
744
 
{
745
 
    PyObject *junk, *a, *b;
746
 
    PatienceSequenceMatcher *self;
747
 
 
748
 
    self = (PatienceSequenceMatcher *)type->tp_alloc(type, 0);
749
 
    if (self != NULL) {
750
 
 
751
 
        if (!PyArg_ParseTuple(args, "OOO", &junk, &a, &b)) {
752
 
            Py_DECREF(self);
753
 
            return NULL;
754
 
        }
755
 
 
756
 
        self->asize = load_lines(a, &(self->a));
757
 
        self->bsize = load_lines(b, &(self->b));
758
 
 
759
 
        if (self->asize == -1 || self->bsize == -1) {
760
 
            Py_DECREF(self);
761
 
            return NULL;
762
 
        }
763
 
 
764
 
        if (!equate_lines(&self->hashtable, self->a, self->b, self->asize, self->bsize)) {
765
 
            Py_DECREF(self);
766
 
            return NULL;
767
 
        }
768
 
 
769
 
        self->backpointers = (Py_ssize_t *)malloc(sizeof(Py_ssize_t) * self->bsize * 4);
770
 
        if (self->backpointers == NULL) {
771
 
            Py_DECREF(self);
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
 
    free(self->b);
787
 
    free(self->a);
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
 
}